{"record":{"id":"1426b6b696f0621a","repo":"getgrav/grav","slug":"bad-storage-class-s","errorCode":null,"errorMessage":"Bad storage class: %s","messagePattern":"Bad storage class: (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Framework/Flex/FlexDirectory.php","lineNumber":1051,"sourceCode":"\n    /**\n     * @return FlexStorageInterface\n     */\n    protected function createStorage(): FlexStorageInterface\n    {\n        $this->collection = $this->createCollection([]);\n\n        $storage = $this->getConfig('data.storage');\n\n        if (!is_array($storage)) {\n            $storage = ['options' => ['folder' => $storage]];\n        }\n\n        $className = $storage['class'] ?? SimpleStorage::class;\n        $options = $storage['options'] ?? [];\n\n        if (!is_a($className, FlexStorageInterface::class, true)) {\n            throw new \\RuntimeException('Bad storage class: ' . $className);\n        }\n\n        return new $className($options);\n    }\n\n    /**\n     * @param string $keyField\n     * @return FlexIndexInterface\n     * @phpstan-return FlexIndexInterface<FlexObjectInterface>\n     */\n    protected function loadIndex(string $keyField): FlexIndexInterface\n    {\n        static $i = 0;\n\n        $index = $this->indexes[$keyField] ?? null;\n        if (null !== $index) {\n            return $index;\n        }","sourceCodeStart":1033,"sourceCodeEnd":1069,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Framework/Flex/FlexDirectory.php#L1033-L1069","documentation":"FlexDirectory::getStorage() (line 1051) lazily builds the directory's storage from the blueprint key data.storage (a folder string, or an array with 'class' and 'options'; default class SimpleStorage). If the resolved class does not implement FlexStorageInterface — including the case where the class simply doesn't exist — it throws 'Bad storage class', and every operation on the directory fails from that point.","triggerScenarios":"Configuring data.storage.class to a custom storage backend that is misspelled, not autoloaded, or not a FlexStorage implementation; switching a type's storage class in an upgrade while cached config still holds the old name; YAML quoting/escaping mistakes in the namespaced class name.","commonSituations":"Custom storage backends (e.g. database-backed) in plugins; plugin upgrades renaming storage classes; stale config cache after changing flex blueprints.","solutions":["Point data.storage.class at an autoloaded class implementing Grav\\Framework\\Flex\\Interfaces\\FlexStorageInterface, or drop the custom class and use a built-in one such as 'Grav\\Framework\\Flex\\Storage\\FileStorage'.","Run composer dump-autoload and clear Grav cache after changing storage configuration.","Verify YAML quoting: single-quoted 'Vendor\\Plugin\\Storage\\DbStorage' with correct backslashes."],"exampleFix":"# before (blueprint.yaml)\ndata:\n  storage:\n    class: Grav/Framework/Flex/Storage/MyStorage  # slashes + class does not exist\n\n# after\ndata:\n  storage:\n    class: 'Grav\\Framework\\Flex\\Storage\\FileStorage'\n    options:\n      folder: user://data/things","handlingStrategy":"validation","validationCode":"$storage = $directory->getConfig('data.storage');\n$class = \\is_array($storage) ? ($storage['class'] ?? \\Grav\\Framework\\Flex\\Storage\\SimpleStorage::class) : \\Grav\\Framework\\Flex\\Storage\\SimpleStorage::class;\nif (!class_exists($class) || !is_a($class, \\Grav\\Framework\\Flex\\Interfaces\\FlexStorageInterface::class, true)) {\n    // fix storage configuration before touching the directory\n}","typeGuard":"function isValidFlexStorageClass(string $class): bool\n{\n    return class_exists($class)\n        && \\is_a($class, \\Grav\\Framework\\Flex\\Interfaces\\FlexStorageInterface::class, true);\n}","tryCatchPattern":"try {\n    $index = $directory->getIndex();\n} catch (\\RuntimeException $e) {\n    if (str_starts_with($e->getMessage(), 'Bad storage class')) {\n        // storage misconfigured: fail loudly with the class name\n    }\n}","preventionTips":["Verify custom storage backends implement FlexStorageInterface in the plugin's boot checks.","Clear Grav cache after changing data.storage settings.","Prefer built-in storage classes unless you truly need a custom backend."],"tags":["flex","configuration","class-loading","storage","blueprint"],"backgroundTag":"invalid-storage-class-config","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}