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

  1. 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'.
  2. Run composer dump-autoload and clear Grav cache after changing storage configuration.
  3. 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

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


AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17). Data as JSON: /api/errors/1426b6b696f0621a. Report an issue: GitHub.