octobercms/october · error · SystemException

Custom widget class [{$widgetClass}] is not set.

Error message

Custom widget class [{$widgetClass}] is not set.

What it means

Dash::onRunCustomWidgetHandler is the AJAX endpoint that runs a handler on a custom (Vue) dashboard widget. It reads widget_config from POST and immediately requires widget_config.widgetClass to be a non-empty value. If the request omits widgetClass, a SystemException is thrown before DashManager is consulted, so the widget is never resolved.

Source

Thrown at modules/dashboard/widgets/Dash.php:612

        $widgetConfig = (array) post('widget_config');

        $dataSource = $this->getRequestedDataSource($widgetConfig);

        return $dataSource->runHandler($handlerName);
    }

    /**
     * onRunCustomWidgetHandler handler
     */
    public function onRunCustomWidgetHandler()
    {
        $handlerName = post('handler');
        $widgetConfig = post('widget_config');
        $extraData = post('extra_data', []);

        $widgetClass = $widgetConfig['widgetClass'];
        if (!$widgetClass) {
            throw new SystemException("Custom widget class [{$widgetClass}] is not set.");
        }

        $widget = DashManager::instance()->getVueReportWidget($widgetClass, $this->controller);
        if (!$widget) {
            throw new SystemException("Widget class [{$widgetClass}] not registered.");
        }

        $result = $widget->runHandler(
            $widgetConfig,
            $handlerName,
            $extraData
        );

        return $result;
    }

    /**
     * makeDataSource

View on GitHub (pinned to b608633a7e)

Solutions

  1. Include the widget's fully qualified class name under widget_config.widgetClass in the AJAX payload.
  2. Send the payload via the framework's data-request / request API so widget_config mirrors the definition the dashboard already has (reuse widget.widgetClass from the widget config object).
  3. If the widget class is correct but still empty, inspect the POST body (network tab) for key casing or nesting errors.
  4. Update the custom widget to extend VueReportWidgetBase so the standard payload builder includes widgetClass.

Example fix

// before (JS payload)
data: { handler: 'onMyAction', widget_config: { alias: 'sales' } }

// after
data: { handler: 'onMyAction', widget_config: { widgetClass: 'MyPlugin\ReportWidgets\SalesWidget', alias: 'sales' } }
Defensive patterns

Strategy: validation

Validate before calling

$widgetConfig = (array) post('widget_config');
if (empty($widgetConfig['widgetClass']) || !is_string($widgetConfig['widgetClass'])) {
    throw new \ValidationException(['widgetClass' => 'widgetClass is required']);
}

Try / catch

try {
    $result = $dash->onRunCustomWidgetHandler();
} catch (\SystemException $e) {
    // payload missing widgetClass: fix the caller, do not retry blindly
    return response()->json(['error' => $e->getMessage()], 422);
}

Prevention

When it happens

Trigger: POSTing to the dashboard's handler endpoint (handler + widget_config) with widget_config missing the widgetClass key or set to an empty string; a custom widget's JavaScript sending {widget_config: {}} ; a renamed widget property in JS not updated on the PHP side.

Common situations: Custom Vue report widget whose request payload was hand-built and forgot widgetClass; front-end/back-end version mismatch after upgrading the dashboard module (payload key renamed); third-party widget copied from an older example that used a different key name.

Related errors


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