octobercms/october · error · SystemException

Handler does not exist

Error message

Handler does not exist

What it means

The second guard in VueReportWidgetBase::runHandler(): after the name passes the on* regex, method_exists($this, $handlerName) must find a public method on the widget class, otherwise SystemException('Handler does not exist') is thrown. This means the name is well-formed but the widget class defines no such method — the handler was renamed, lives on a different class, or the wrong widget class is handling the request.

Source

Thrown at modules/dashboard/classes/VueReportWidgetBase.php:73

        $this->controller->registerVueComponent($this::class);
    }

    /**
     * getData
     */
    abstract public function getData(ReportFetchData $data): mixed;

    /**
     * runHandler
     */
    public function runHandler(array $widgetConfig, string $handlerName, array $extraData): mixed
    {
        if (!preg_match('/^on[a-z0-9_]+/i', $handlerName)) {
            throw new SystemException('Invalid handler name');
        }

        if (!method_exists($this, $handlerName)) {
            throw new SystemException('Handler does not exist');
        }

        return $this->{$handlerName}($widgetConfig, $extraData);
    }
}

View on GitHub (pinned to b608633a7e)

Solutions

  1. Add or restore the public method with the exact requested name on the widget class: public function onLoadData(array $widgetConfig, array $extraData): mixed.
  2. Update the JS/front-end call to the current method name, and bust cached front-end assets after deploys.
  3. Verify the request is routed to the widget class that actually defines the handler (check the widget alias/class in the AJAX payload).
  4. If the handler moved, keep a deprecated onOldName() wrapper delegating to the new one during transition.

Example fix

// before
// JS: this.requestHandler('onLoadData') but the class only has onRefreshData()

// after
public function onLoadData(array $widgetConfig, array $extraData): mixed
{
    return $this->onRefreshData($widgetConfig, $extraData);
}
Defensive patterns

Strategy: type-guard

Validate before calling

$handler = (string) $request->input('handler', '');
if (!preg_match('/^on[a-z0-9_]+$/i', $handler) || !method_exists($widget, $handler)) {
    throw new ApplicationException("Unknown handler '{$handler}' for widget " . get_class($widget));
}
$widget->runHandler($config, $handler, $extra);

Type guard

function widgetHasHandler(VueReportWidgetBase $widget, string $name): bool
{
    return preg_match('/^on[a-z0-9_]+$/i', $name) === 1
        && method_exists($widget, $name);
}

Try / catch

try {
    $result = $widget->runHandler($config, $handler, $extra);
} catch (SystemException $e) {
    if (str_contains($e->getMessage(), 'Handler does not exist')) {
        return response()->json(['error' => 'Unknown handler'], 422);
    }
    throw $e;
}

Prevention

When it happens

Trigger: Front-end still calls 'onRefreshData' after the method was renamed to 'onLoadData'; the AJAX route resolves to a different VueReportWidget subclass than the one containing the handler (wrong widget alias/class in the request); method defined but on a trait not used by the class, or defined as private/protected in a scope method_exists still finds but invocation may fail — check visibility too.

Common situations: Renaming a handler in PHP without updating the JS call site; duplicating a widget and forgetting to port its handlers; plugin version skew between front-end assets (cached JS) and back-end code.

Related errors


AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21). Data as JSON: /api/errors/ef63a4857856e71d. Report an issue: GitHub.