phalcon/cphalcon · error · Phalcon\Auth\Exceptions\MissingHandlerContext
The Acl access gate requires the 'handler' context key to de
Error message
The Acl access gate requires the 'handler' context key to determine the ACL component
What it means
Phalcon\Auth\Access\Acl derives the ACL component name to check from the dispatch context: by default the 'handler' (controller) name, optionally prefixed with 'module' + separator. If the context array carries no non-empty string 'handler' key there is no component to authorize against, so the gate throws MissingHandlerContext rather than guessing.
Source
Thrown at phalcon/Auth/Access/Acl.zep:82
* @phpstan-param AccessContext $context
*
* @throws Exception
*/
public function isAllowed(<Guard> guard, string actionName, array context = []) -> bool
{
var component, handler, module, params;
if (in_array(actionName, this->exceptActions, true)) {
return true;
}
if (!empty(this->onlyActions) && !in_array(actionName, this->onlyActions, true)) {
return true;
}
fetch handler, context["handler"];
if (typeof handler !== "string" || handler === "") {
throw new MissingHandlerContext();
}
let component = handler;
fetch module, context["module"];
if (typeof module === "string" && module !== "") {
let component = module . this->moduleSeparator . handler;
}
let params = null;
fetch params, context["params"];
if (typeof params !== "array") {
let params = null;
}
return this->acl->isAllowed(
this->resolveRole(guard),View on GitHub (pinned to b7419de9cd)
Solutions
- Pass a full context: `$access->isAllowed($guard, 'index', ['handler' => 'invoices', 'module' => 'admin', 'params' => []])`
- In a custom listener, build the context from the dispatcher: handler = controller name (or controller class), module = dispatcher->getModuleName(), params = dispatcher->getParams()
- Validate the context shape at the boundary of your listener so a bad context fails with your own clear error
Example fix
// before
$allowed = $access->isAllowed($guard, $actionName, ['module' => $module]);
// after
$allowed = $access->isAllowed($guard, $actionName, [
'handler' => $dispatcher->getControllerName(),
'module' => $dispatcher->getModuleName(),
'params' => $dispatcher->getParams(),
]); Defensive patterns
Strategy: validation
Validate before calling
if (!isset($context['handler']) || !is_string($context['handler']) || $context['handler'] === '') {
throw new InvalidArgumentException('Access context requires a non-empty string handler key');
}
$allowed = $access->isAllowed($guard, $actionName, $context); Type guard
function isValidAccessContext(array $context): bool
{
return isset($context['handler']) && is_string($context['handler']) && $context['handler'] !== '';
} Try / catch
try {
$allowed = $access->isAllowed($guard, $actionName, $context);
} catch (\Phalcon\Auth\Exceptions\MissingHandlerContext $e) {
$logger->error('Access context built without handler; check dispatcher listener wiring');
throw $e;
} Prevention
- Build the access context in exactly one place from the dispatcher (handler, module, params)
- Validate the context shape in your listener before delegating to the gate
- Cover custom listeners with a test asserting the gate receives a handler key
When it happens
Trigger: Calling `$access->isAllowed($guard, $actionName, $context)` where $context lacks 'handler' or sets it to '' / a non-string — most often a custom dispatcher listener that builds its own context array instead of forwarding the one carrying handler (controller name), module and params.
Common situations: Subclassing AbstractAuthDispatcherListener and passing an empty or partial context in beforeDispatch/afterDispatch hooks; invoking the Acl gate manually in tests or console commands with a hand-made context.
Related errors
- Access denied for {type} '{name}'
- Authenticated user does not implement 'Phalcon\Acl\RoleAware
- Access '{accessName}' is not registered
- Malformed ACL snapshot structure
- The role name cannot be '*'
AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21).
Data as JSON: /api/errors/49c15cd134f9e1e8.
Report an issue: GitHub.