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;
}
/**
* makeDataSourceView on GitHub (pinned to b608633a7e)
Solutions
- Include the widget's fully qualified class name under widget_config.widgetClass in the AJAX payload.
- 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).
- If the widget class is correct but still empty, inspect the POST body (network tab) for key casing or nesting errors.
- 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
- Always build widget_config from the widget's own config object so widgetClass is included.
- Add a required-field check in JS before firing the AJAX handler.
- Log the exact POST payload when this fires to spot renamed keys quickly.
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
- Custom widget class [{$widgetClass}] is not set.
- The property name is not specified.
- Trying to get selected row without a popup reference.
- Unknown widget report interval {$widgetInterval}
- The provided class is not a report widget:
AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21).
Data as JSON: /api/errors/44fd336c11731cd4.
Report an issue: GitHub.