yiisoft/yii2 · error · InvalidConfigException

get_class($this) . ' must define a "run()" method.'

Error message

get_class($this) . ' must define a "run()" method.'

What it means

Standalone actions (classes extending yii\base\Action, registered in a controller's actions() map) are executed via runWithParams(): it first checks method_exists($this, 'run') and throws InvalidConfigException(get_class($this) . ' must define a "run()" method.') before parameter binding, beforeRun()/afterRun(), and events. An Action without run() is unusable, so the throw fires on the first request to that action.

Source

Thrown at framework/base/Action.php:88

     * @return string the unique ID of this action among the whole application.
     */
    public function getUniqueId()
    {
        return $this->controller->getUniqueId() . '/' . $this->id;
    }

    /**
     * Runs this action with the specified parameters.
     * This method is mainly invoked by the controller.
     *
     * @param array $params the parameters to be bound to the action's run() method.
     * @return mixed the result of the action
     * @throws InvalidConfigException if the action class does not have a run() method
     */
    public function runWithParams($params)
    {
        if (!method_exists($this, 'run')) {
            throw new InvalidConfigException(get_class($this) . ' must define a "run()" method.');
        }
        $args = $this->controller->bindActionParams($this, $params);
        Yii::debug('Running action: ' . get_class($this) . '::run(), invoked by ' . get_class($this->controller), __METHOD__);
        if (Yii::$app->requestedParams === null) {
            Yii::$app->requestedParams = $args;
        }
        if ($this->beforeRun()) {
            $result = call_user_func_array([$this, 'run'], $args);
            $this->afterRun();

            return $result;
        }

        return null;
    }

    /**
     * This method is called right before `run()` is executed.

View on GitHub (pinned to 66f00d18a2)

Solutions

  1. Add 'public function run()' to the class named in the exception message and return the response/result from it
  2. If the class is a shared base, mark it abstract and implement run() in each concrete subclass - never register the base class in actions()
  3. If you actually wanted an inline action, delete the class and add action<Id>() to the controller instead

Example fix

// before
class DownloadAction extends \yii\base\Action
{
    public function execute() { /* ... */ } // wrong method name
}

// after
class DownloadAction extends \yii\base\Action
{
    public function run()
    {
        return $this->controller->response->sendFile($this->path);
    }
}
Defensive patterns

Strategy: validation

Validate before calling

foreach ($this->actions() as $id => $definition) {
    $class = is_array($definition) ? ($definition['class'] ?? null) : $definition;
    if (is_string($class) && !method_exists($class, 'run')) {
        throw new \LogicException("Action '{$class}' registered as '{$id}' has no run() method");
    }
}

Try / catch

try {
    return $action->runWithParams($params);
} catch (\yii\base\InvalidConfigException $e) {
    Yii::error($e->getMessage());
    throw $e;
}

Prevention

When it happens

Trigger: A custom action class listed in actions() whose logic method is named execute() or handle() instead of run(); an abstract base Action class mistakenly registered directly in actions(); renaming run() during a refactor of a previously working action.

Common situations: Action classes ported from other frameworks (Symfony uses execute()); vendor Action base classes whose run() was removed and subclasses are expected to add it; scaffolding that generated an empty action body.

Related errors


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