getgrav/grav · error · RuntimeException

Bad collection class: %s

Error message

Bad collection class: %s

What it means

FlexDirectory::createCollection() (line 595) builds collections using the class from the blueprint key data.collection (default GenericCollection), verifying with is_a(..., FlexCollectionInterface::class, true) first. If the configured name is misspelled, not autoloaded, or does not implement the interface, it throws 'Bad collection class'. Like the object check, a non-existent class string fails is_a() and produces the same message.

Source

Thrown at system/src/Grav/Framework/Flex/FlexDirectory.php:595

        if (!is_a($className, FlexObjectInterface::class, true)) {
            throw new \RuntimeException('Bad object class: ' . $className);
        }

        return new $className($data, $key, $this, $validate);
    }

    /**
     * @param array $entries
     * @param string|null $keyField
     * @return FlexCollectionInterface
     * @phpstan-return FlexCollectionInterface<FlexObjectInterface>
     */
    public function createCollection(array $entries, ?string $keyField = null): FlexCollectionInterface
    {
        /** phpstan-var class-string $className */
        $className = $this->collectionClassName ?: $this->getCollectionClass();
        if (!is_a($className, FlexCollectionInterface::class, true)) {
            throw new \RuntimeException('Bad collection class: ' . $className);
        }

        return $className::createFromArray($entries, $this, $keyField);
    }

    /**
     * @param array $entries
     * @param string|null $keyField
     * @return FlexIndexInterface
     * @phpstan-return FlexIndexInterface<FlexObjectInterface>
     */
    public function createIndex(array $entries, ?string $keyField = null): FlexIndexInterface
    {
        /** @phpstan-var class-string $className */
        $className = $this->indexClassName ?: $this->getIndexClass();
        if (!is_a($className, FlexIndexInterface::class, true)) {
            throw new \RuntimeException('Bad index class: ' . $className);
        }

View on GitHub (pinned to 6040efed04)

Solutions

  1. Point data.collection at an autoloaded class implementing Grav\Framework\Flex\Interfaces\FlexCollectionInterface (normally extending Grav\Framework\Flex\FlexCollection).
  2. Confirm runtime autoloadability (composer dump-autoload; plugin enabled).
  3. Use single-quoted YAML class names and re-check backslash escaping.

Example fix

# before (blueprint.yaml)
data:
  collection: 'Vendor\Plugin\Flex\ThingsCollection'  # class does not exist / wrong interface

# after
data:
  collection: 'Grav\Framework\Flex\GenericCollection'
Defensive patterns

Strategy: validation

Validate before calling

$class = $directory->getConfig('data.collection', \Grav\Framework\Flex\GenericCollection::class);
if (!class_exists($class) || !is_a($class, \Grav\Framework\Flex\Interfaces\FlexCollectionInterface::class, true)) {
    // fix blueprint before calling getCollection()/createCollection()
}

Type guard

function isValidFlexCollectionClass(string $class): bool
{
    return class_exists($class)
        && \is_a($class, \Grav\Framework\Flex\Interfaces\FlexCollectionInterface::class, true);
}

Try / catch

try {
    $collection = $directory->createCollection($entries);
} catch (\RuntimeException $e) {
    if (str_starts_with($e->getMessage(), 'Bad collection class')) {
        // configuration defect: log the class name and stop
    }
}

Prevention

When it happens

Trigger: A custom flex type whose data.collection entry names a class that is not a FlexCollection implementation; renaming a collection class without updating the blueprint; YAML escaping errors doubling backslashes; the providing plugin being disabled so its autoloader no longer resolves the class.

Common situations: Custom flex types with specialized collection classes; plugin refactors; blueprint overrides in user:// config that stale-copy an old class name.

Related errors


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