{"record":{"id":"49c15cd134f9e1e8","repo":"phalcon/cphalcon","slug":"the-acl-access-gate-requires-the-handler-context","errorCode":null,"errorMessage":"The Acl access gate requires the 'handler' context key to determine the ACL component","messagePattern":"The Acl access gate requires the 'handler' context key to determine the ACL component","errorType":"exception","errorClass":"Phalcon\\Auth\\Exceptions\\MissingHandlerContext","httpStatus":null,"severity":"error","filePath":"phalcon/Auth/Access/Acl.zep","lineNumber":82,"sourceCode":"     * @phpstan-param AccessContext $context\n     *\n     * @throws Exception\n     */\n    public function isAllowed(<Guard> guard, string actionName, array context = []) -> bool\n    {\n        var component, handler, module, params;\n\n        if (in_array(actionName, this->exceptActions, true)) {\n            return true;\n        }\n\n        if (!empty(this->onlyActions) && !in_array(actionName, this->onlyActions, true)) {\n            return true;\n        }\n\n        fetch handler, context[\"handler\"];\n        if (typeof handler !== \"string\" || handler === \"\") {\n            throw new MissingHandlerContext();\n        }\n\n        let component = handler;\n\n        fetch module, context[\"module\"];\n        if (typeof module === \"string\" && module !== \"\") {\n            let component = module . this->moduleSeparator . handler;\n        }\n\n        let params = null;\n\n        fetch params, context[\"params\"];\n        if (typeof params !== \"array\") {\n            let params = null;\n        }\n\n        return this->acl->isAllowed(\n            this->resolveRole(guard),","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/Auth/Access/Acl.zep#L64-L100","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n$allowed = $access->isAllowed($guard, $actionName, ['module' => $module]);\n// after\n$allowed = $access->isAllowed($guard, $actionName, [\n    'handler' => $dispatcher->getControllerName(),\n    'module'  => $dispatcher->getModuleName(),\n    'params'  => $dispatcher->getParams(),\n]);","handlingStrategy":"validation","validationCode":"if (!isset($context['handler']) || !is_string($context['handler']) || $context['handler'] === '') {\n    throw new InvalidArgumentException('Access context requires a non-empty string handler key');\n}\n$allowed = $access->isAllowed($guard, $actionName, $context);","typeGuard":"function isValidAccessContext(array $context): bool\n{\n    return isset($context['handler']) && is_string($context['handler']) && $context['handler'] !== '';\n}","tryCatchPattern":"try {\n    $allowed = $access->isAllowed($guard, $actionName, $context);\n} catch (\\Phalcon\\Auth\\Exceptions\\MissingHandlerContext $e) {\n    $logger->error('Access context built without handler; check dispatcher listener wiring');\n    throw $e;\n}","preventionTips":["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"],"tags":["auth","acl","context","dispatcher"],"backgroundTag":"missing-context-key","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}