yiisoft/yii2 · error · InvalidConfigException
Controller class must extend from \yii\base\Controller.
Error message
Controller class must extend from \yii\base\Controller.
What it means
In yii\base\Module::createControllerByID(), a controller class name was successfully derived (controllerNamespace + prefix + CamelCase ID + 'Controller'), contains no dash, and class_exists() is true — but is_subclass_of($className, 'yii\base\Controller') is false. Under YII_DEBUG this throws InvalidConfigException immediately; with debug off the method returns null and the failure surfaces later as the generic 'Unable to resolve the request' InvalidRouteException.
Source
Thrown at framework/base/Module.php:682
}
if ($this->isIncorrectClassNameOrPrefix($className, $prefix)) {
return null;
}
$className = preg_replace_callback('%-([a-z0-9_])%i', function ($matches) {
return ucfirst($matches[1]);
}, ucfirst($className)) . 'Controller';
$className = ltrim($this->controllerNamespace . '\\' . str_replace('/', '\\', $prefix) . $className, '\\');
if (strpos($className, '-') !== false || !class_exists($className)) {
return null;
}
if (is_subclass_of($className, 'yii\base\Controller')) {
$controller = Yii::createObject($className, [$id, $this]);
return get_class($controller) === $className ? $controller : null;
} elseif (YII_DEBUG) {
throw new InvalidConfigException('Controller class must extend from \\yii\\base\\Controller.');
}
return null;
}
/**
* Checks if class name or prefix is incorrect
*
* @param string $className
* @param string $prefix
* @return bool
*/
private function isIncorrectClassNameOrPrefix($className, $prefix)
{
if (!preg_match('%^[a-z][a-z0-9\\-_]*$%', $className)) {
return true;
}
if ($prefix !== '' && !preg_match('%^[a-z0-9_/]+$%i', $prefix)) {View on GitHub (pinned to 66f00d18a2)
Solutions
- Open the derived class named by the exception and change its parent to \yii\web\Controller (web) or \yii\console\Controller (console)
- Rename or remove the colliding same-named class in that namespace / autoload mapping
- Fix the use statement or composer classmap so autoloading resolves the intended controller file
- Reproduce with YII_DEBUG enabled locally — production masks this as a plain route-resolution error
Example fix
// before
namespace app\controllers;
class SiteController extends \yii\db\ActiveRecord // wrong base class
{
}
// after
namespace app\controllers;
class SiteController extends \yii\web\Controller
{
} Defensive patterns
Strategy: type-guard
Validate before calling
$className = 'app\\controllers\\' . str_replace(' ', '', ucwords(str_replace('-', ' ', $id))) . 'Controller';
if (class_exists($className) && !is_subclass_of($className, \yii\base\Controller::class)) {
throw new \RuntimeException($className . ' does not extend yii\\base\\Controller');
} Type guard
function isControllerClass(string $class): bool
{
return class_exists($class) && is_subclass_of($class, \yii\base\Controller::class);
} Try / catch
try {
Yii::$app->runAction($route);
} catch (\yii\base\InvalidConfigException $e) {
// YII_DEBUG-only signal that a controller class has the wrong parent — fix code, do not catch in prod
} Prevention
- Standardize controller scaffolds so extends always targets \yii\web\Controller or \yii\console\Controller
- Run a CI check asserting every *Controller class under the controller path is a Controller subclass
- Keep controller namespaces free of non-controller classes with Controller-suffixed names
When it happens
Trigger: A class with the derived name exists in the controller namespace but does not extend the framework base — e.g. a helper/model named FooController, or a scaffolded controller extending the wrong parent (copy-paste left \yii\db\ActiveRecord or another class in extends); an IDE auto-import resolving 'Controller' to a non-controller class; composer classmap/alias loading a same-named class from a different directory.
Common situations: Code generation tools emitting plain classes; copy-paste of controller scaffolds followed by editing the extends clause; name collisions after adding vendor packages; a use statement importing a project-local Controller that is not itself a descendant of yii\base\Controller.
Related errors
- Unknown scenario: $scenario
- Invalid validation rule: a rule must specify both attribute
- The directory does not exist: $path
- Unable to determine the entry script file path.
- Encryption requires the OpenSSL PHP extension
AI-assisted analysis of yiisoft/yii2@66f00d18a2 (2026-08-17).
Data as JSON: /api/errors/4c7e610793f073e9.
Report an issue: GitHub.