getgrav/grav · error · RuntimeException
Bad storage class: %s
Error message
Bad storage class: %s
What it means
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.
Source
Thrown at system/src/Grav/Framework/Flex/FlexDirectory.php:1051
/**
* @return FlexStorageInterface
*/
protected function createStorage(): FlexStorageInterface
{
$this->collection = $this->createCollection([]);
$storage = $this->getConfig('data.storage');
if (!is_array($storage)) {
$storage = ['options' => ['folder' => $storage]];
}
$className = $storage['class'] ?? SimpleStorage::class;
$options = $storage['options'] ?? [];
if (!is_a($className, FlexStorageInterface::class, true)) {
throw new \RuntimeException('Bad storage class: ' . $className);
}
return new $className($options);
}
/**
* @param string $keyField
* @return FlexIndexInterface
* @phpstan-return FlexIndexInterface<FlexObjectInterface>
*/
protected function loadIndex(string $keyField): FlexIndexInterface
{
static $i = 0;
$index = $this->indexes[$keyField] ?? null;
if (null !== $index) {
return $index;
}View on GitHub (pinned to 6040efed04)
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.
Example fix
# before (blueprint.yaml)
data:
storage:
class: Grav/Framework/Flex/Storage/MyStorage # slashes + class does not exist
# after
data:
storage:
class: 'Grav\Framework\Flex\Storage\FileStorage'
options:
folder: user://data/things Defensive patterns
Strategy: validation
Validate before calling
$storage = $directory->getConfig('data.storage');
$class = \is_array($storage) ? ($storage['class'] ?? \Grav\Framework\Flex\Storage\SimpleStorage::class) : \Grav\Framework\Flex\Storage\SimpleStorage::class;
if (!class_exists($class) || !is_a($class, \Grav\Framework\Flex\Interfaces\FlexStorageInterface::class, true)) {
// fix storage configuration before touching the directory
} Type guard
function isValidFlexStorageClass(string $class): bool
{
return class_exists($class)
&& \is_a($class, \Grav\Framework\Flex\Interfaces\FlexStorageInterface::class, true);
} Try / catch
try {
$index = $directory->getIndex();
} catch (\RuntimeException $e) {
if (str_starts_with($e->getMessage(), 'Bad storage class')) {
// storage misconfigured: fail loudly with the class name
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Custom storage backends (e.g. database-backed) in plugins; plugin upgrades renaming storage classes; stale config cache after changing flex blueprints.
Related errors
- Bad object class: %s
- Bad collection class: %s
- Bad index class: %s
- Unknown extension type
- Bad Data Formatter
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/1426b6b696f0621a.
Report an issue: GitHub.