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

  1. Open the derived class named by the exception and change its parent to \yii\web\Controller (web) or \yii\console\Controller (console)
  2. Rename or remove the colliding same-named class in that namespace / autoload mapping
  3. Fix the use statement or composer classmap so autoloading resolves the intended controller file
  4. 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

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


AI-assisted analysis of yiisoft/yii2@66f00d18a2 (2026-08-17). Data as JSON: /api/errors/4c7e610793f073e9. Report an issue: GitHub.