{"record":{"id":"c3edd89104471f11","repo":"octobercms/october","slug":"dynamic-inspector-control-options-cannot-be-loaded","errorCode":null,"errorMessage":"Dynamic Inspector control options cannot be loaded for the specified class.","messagePattern":"Dynamic Inspector control options cannot be loaded for the specified class\\.","errorType":"exception","errorClass":"ApplicationException","httpStatus":null,"severity":"error","filePath":"modules/backend/traits/InspectableContainer.php","lineNumber":35,"sourceCode":"     */\n    public function onInspectableGetOptions()\n    {\n        // Disable asset broadcasting\n        $this->flushAssets();\n\n        $property = trim(post('inspectorProperty'));\n        if (!$property) {\n            throw new ApplicationException('The property name is not specified.');\n        }\n\n        $className = trim(post('inspectorClassName'));\n        if (!$className || $className === 'undefined') {\n            throw new ApplicationException('The inspectable class name is not specified.');\n        }\n\n        $traitFound = in_array(\\System\\Traits\\PropertyContainer::class, class_uses_recursive($className));\n        if (!$traitFound) {\n            throw new ApplicationException('Dynamic Inspector control options cannot be loaded for the specified class.');\n        }\n\n        $obj = new $className(null);\n        $obj->setProperties(post());\n\n        // Nested properties have names like object.property.\n        // Convert them to Object.Property.\n        $propertyNameParts = explode('.', $property);\n        $propertyMethodName = '';\n        foreach ($propertyNameParts as $part) {\n            $part = trim($part);\n\n            if (!strlen($part)) {\n                continue;\n            }\n\n            $propertyMethodName .= ucfirst($part);\n        }","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/octobercms/october/blob/b608633a7e8922487d91a8161499020121c3b3bf/modules/backend/traits/InspectableContainer.php#L17-L53","documentation":"onInspectableGetOptions only resolves dynamic options for classes whose hierarchy includes System\\Traits\\PropertyContainer, verified with class_uses_recursive(). Any other class — even one that exists and autoloads — is rejected, because the handler instantiates the class and calls property APIs that only that trait provides. This is a server-side contract check on the class named by inspectorClassName.","triggerScenarios":"POST with a valid inspectorProperty and an existing inspectorClassName whose class tree does not include System\\Traits\\PropertyContainer — for example a plain PHP helper class, a DTO, or a model.","commonSituations":"Pointing an inspector control's dynamic options at a helper/DTO instead of a component; plugin authors exposing a new inspectable class but forgetting the trait; upgrades that restructured base classes and dropped the trait from the hierarchy.","solutions":["Make the target class use System\\Traits\\PropertyContainer — directly, or by extending a base like Cms\\Classes\\ComponentBase that already includes it.","Double-check the fully-qualified class name posted as inspectorClassName; a wrong-but-existing class fails exactly here.","After moving or renaming classes, run composer dump-autoload and clear caches."],"exampleFix":"// before\nclass StockReports\n{\n}\n\n// after\nclass StockReports\n{\n    use \\System\\Traits\\PropertyContainer;\n}\n\n// or extend a base that already provides the trait\nclass StockReports extends \\Cms\\Classes\\ComponentBase\n{\n}","handlingStrategy":"validation","validationCode":"// Before pointing an inspector at a class, verify the contract server-side\n$usable = class_exists($className)\n    && in_array(\\System\\Traits\\PropertyContainer::class, class_uses_recursive($className), true);\n\nif (!$usable) {\n    // choose a class that uses PropertyContainer, e.g. a ComponentBase\n}","typeGuard":"function isPropertyContainerClass(string $class): bool\n{\n    return class_exists($class)\n        && in_array(\\System\\Traits\\PropertyContainer::class, class_uses_recursive($class), true);\n}","tryCatchPattern":null,"preventionTips":["Only use ComponentBase/WidgetBase-style classes as dynamic-options providers","Add a boot-time assertion for inspectable classes referenced in inspector configs","Re-run the class_uses_recursive check in tests after upgrading the CMS"],"tags":["inspector","php-trait","class-validation","dynamic-options"],"backgroundTag":"missing-trait","analyzedSha":"b608633a7e8922487d91a8161499020121c3b3bf","analyzedAt":"2026-08-21T04:24:57.515Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}