{"record":{"id":"40b212c2fc806ae7","repo":"cakephp/cakephp","slug":"controller-must-be-set-first","errorCode":null,"errorMessage":"Controller must be set first.","messagePattern":"Controller must be set first\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/Controller/ComponentRegistry.php","lineNumber":101,"sourceCode":"     * @return $this\n     */\n    public function setController(Controller $controller)\n    {\n        $this->_Controller = $controller;\n        $this->setEventManager($controller->getEventManager());\n\n        return $this;\n    }\n\n    /**\n     * Get the controller associated with the collection.\n     *\n     * @return \\Cake\\Controller\\Controller Controller instance.\n     */\n    public function getController(): Controller\n    {\n        if ($this->_Controller === null) {\n            throw new RuntimeException('Controller must be set first.');\n        }\n\n        return $this->_Controller;\n    }\n\n    /**\n     * Resolve a component classname.\n     *\n     * Part of the template method for {@link \\Cake\\Core\\ObjectRegistry::load()}.\n     *\n     * @param string $class Partial classname to resolve.\n     * @return class-string<\\Cake\\Controller\\Component>|null Either the correct class name or null.\n     */\n    protected function _resolveClassName(string $class): ?string\n    {\n        /** @var class-string<\\Cake\\Controller\\Component>|null */\n        return App::className($class, 'Controller/Component', 'Component');\n    }","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/cakephp/cakephp/blob/1128eba9b09f1946df684811350e26f2cef68fac/src/Controller/ComponentRegistry.php#L83-L119","documentation":"ComponentRegistry::getController() is a typed accessor that throws RuntimeException when the registry's controller has never been assigned via setController(). Components are created against a controller, so using the registry before that wiring is a programming-order bug.","triggerScenarios":"Accessing getController() from a component or registry consumer during construction/bootstrap before setController() was called, or using a ComponentRegistry created manually without a controller.","commonSituations":"Using components outside a controller context (shell/console, tests instantiating ComponentRegistry directly), event handlers firing before the controller is attached.","solutions":["Ensure setController() is called before any code reads getController()","Only access the controller in Component::initialize()/later lifecycle hooks, not in the constructor","If using the registry standalone, inject a controller instance first","In tests, construct the Controller and call setController() before creating components"],"exampleFix":"// before\n$registry = new ComponentRegistry();\n$registry->getController(); // throws\n// after\n$controller = new ArticlesController($request, $response);\n$registry = new ComponentRegistry($controller);\n$registry->getController();","handlingStrategy":"type-guard","validationCode":"if ($registry->getController() === null) { /* older API */ }\n// prefer checking before use in component initialize():\nif (!$this->getController()) { return; }","typeGuard":"function hasController(ComponentRegistry $r): bool {\n    try { $r->getController(); return true; } catch (\\RuntimeException $e) { return false; }\n}","tryCatchPattern":"try {\n    $controller = $registry->getController();\n} catch (\\RuntimeException $e) {\n    return; // not in a controller context (shell/test)\n}","preventionTips":["Access the controller only in initialize() or later lifecycle hooks","Never touch the registry from component constructors","In standalone usage, always call setController() immediately after construction"],"tags":["php","cakephp","components","lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"1128eba9b09f1946df684811350e26f2cef68fac","analyzedAt":"2026-09-12T12:07:00.388Z","contentChangedAt":"2026-09-12T12:07:00.388Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}