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
- Add 'public function run()' to the class named in the exception message and return the response/result from it
- If the class is a shared base, mark it abstract and implement run() in each concrete subclass - never register the base class in actions()
- 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
- Cover every actions() entry with a smoke test that runs the route once
- Use static analysis rules that flag Action subclasses without run()
- Prefer a template-method base: final run() in the base class, subclasses override step methods
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.