{"record":{"id":"39afcaac427eaf4f","repo":"octobercms/october","slug":"the-provided-class-is-not-a-report-data-source","errorCode":null,"errorMessage":"The provided class is not a report data source: ","messagePattern":"The provided class is not a report data source: ","errorType":"exception","errorClass":"SystemException","httpStatus":null,"severity":"error","filePath":"modules/dashboard/classes/dashmanager/HasDataSources.php","lineNumber":46,"sourceCode":"        $this->dataSources[$className] = [\n            'displayName' => $displayName\n        ];\n    }\n\n    /**\n     * getDataSource returns a data source instance by its class name.\n     * @throws SystemException if the provided class name is not a subclass Dashboard\\Classes\\ReportDataSourceBase.\n     * @param string $className A data source class name.\n     * @return ?ReportDataSourceBase Returns the data source instance or null.\n     */\n    public function getDataSource(string $className): ?ReportDataSourceBase\n    {\n        if (!array_key_exists($className, $this->dataSources)) {\n            return null;\n        }\n\n        if (!is_subclass_of($className, ReportDataSourceBase::class)) {\n            throw new SystemException(\"The provided class is not a report data source: \" . $className);\n        }\n\n        return new $className();\n    }\n\n    /**\n     * listDataSourceClasses returns class and display names of registered data sources.\n     * @return array\n     */\n    public function listDataSourceClasses(): array\n    {\n        $result = [];\n        foreach ($this->dataSources as $className => $info) {\n            $result[$className] = $info['displayName'];\n        }\n        return $result;\n    }\n","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/octobercms/october/blob/b608633a7e8922487d91a8161499020121c3b3bf/modules/dashboard/classes/dashmanager/HasDataSources.php#L28-L64","documentation":"HasDataSources::getDataSource(string $className) only instantiates classes that were previously registered in the manager's $dataSources array; the method then double-checks is_subclass_of($className, ReportDataSourceBase::class) and throws SystemException('The provided class is not a report data source: <class>') if the registered entry is not a ReportDataSourceBase subclass. Because registration happens via registerDataSource, this fires when a plugin registered a non-data-source class (or the class changed parent) — not when the class is merely unknown (unknown returns null).","triggerScenarios":"Calling DashManager's registerDataSource with a plain class, an interface name, or a class that extends something else, then getDataSource($thatClass); refactoring a data source to no longer extend ReportDataSourceBase while leaving it registered; registering a string that resolves to a trait or abstract helper.","commonSituations":"Plugin boot code copy-pasted with the wrong class name into registerDataSource(); inheritance changed during an upgrade (class now extends a different base); typo in the namespace making is_subclass_of fail.","solutions":["Make the registered class extend Dashboard\\Classes\\ReportDataSourceBase and implement its abstract members (defineColumns/defineMetrics etc.).","Fix the class string passed to registerDataSource() to reference the intended, existing data source class.","Only register data sources at plugin boot (Plugin::boot / registerDashboards) and remove stray registrations of helpers or models.","Check parent class changes after upgrading the dashboard module and restore the correct base class."],"exampleFix":"// before\n$manager->registerDataSource(\\MyPlugin\\Classes\\OrderHelper::class);\n$manager->getDataSource(\\MyPlugin\\Classes\\OrderHelper::class); // throws\n\n// after\nclass OrdersDataSource extends \\Dashboard\\Classes\\ReportDataSourceBase { /* ... */ }\n$manager->registerDataSource(OrdersDataSource::class);","handlingStrategy":"type-guard","validationCode":"if (!class_exists($className) || !is_subclass_of($className, ReportDataSourceBase::class)) {\n    throw new InvalidArgumentException(\"{$className} is not a report data source.\");\n}\n$source = $manager->getDataSource($className);","typeGuard":"function isReportDataSourceClass(mixed $class): bool\n{\n    return is_string($class) && class_exists($class) && is_subclass_of($class, ReportDataSourceBase::class);\n}","tryCatchPattern":"try {\n    $source = $manager->getDataSource($className);\n} catch (SystemException $e) {\n    Log::error($e->getMessage());\n    // skip misregistered source, continue with remaining dashboards\n    $source = null;\n}","preventionTips":["Register data sources only in plugin boot code, referencing classes that extend ReportDataSourceBase.","Re-check parent classes after upgrading the dashboard module.","Add a boot-time assertion (once per environment) that all registered data sources pass is_subclass_of."],"tags":["dashboard","data-source","class-resolution","plugin-registration"],"backgroundTag":"class-not-subtype-of-expected-base","analyzedSha":"b608633a7e8922487d91a8161499020121c3b3bf","analyzedAt":"2026-08-21T04:24:57.515Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}