getgrav/grav · error · RuntimeException

Bad object class: %s

Error message

Bad object class: %s

What it means

FlexDirectory::createObject() (line 578) instantiates the directory's object class, configured via the flex type blueprint key data.object (default GenericObject). Before constructing, is_a($className, FlexObjectInterface::class, true) verifies the class implements that interface; a typo'd, missing, or non-Flex class string throws 'Bad object class'. Note is_a() on a class string that does not exist also returns false, so a non-autoloadable name produces the same error.

Source

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

        if (null === $this->storage) {
            $this->storage = $this->createStorage();
        }

        return $this->storage;
    }

    /**
     * @param array $data
     * @param string $key
     * @param bool $validate
     * @return FlexObjectInterface
     */
    public function createObject(array $data, string $key = '', bool $validate = false): FlexObjectInterface
    {
        /** @phpstan-var class-string $className */
        $className = $this->objectClassName ?: $this->getObjectClass();
        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);
        }

View on GitHub (pinned to 6040efed04)

Solutions

  1. Point data.object at an autoloaded class implementing Grav\Framework\Flex\Interfaces\FlexObjectInterface — in practice, one extending Grav\Framework\Flex\FlexObject.
  2. Verify the class loads at runtime: composer dump-autoload and confirm the owning plugin is enabled, since a non-existent class triggers the same error.
  3. In YAML use single-quoted namespace notation ('Vendor\Plugin\Flex\Thing') and check for doubled backslashes.

Example fix

# before (blueprint.yaml)
data:
  object: Vendor/Plugin/Flex/Thing   # wrong separators — not a class name

# after
data:
  object: 'Vendor\Plugin\Flex\ThingObject'
Defensive patterns

Strategy: validation

Validate before calling

$class = $directory->getConfig('data.object', \Grav\Framework\Flex\GenericObject::class);
if (!class_exists($class) || !is_a($class, \Grav\Framework\Flex\Interfaces\FlexObjectInterface::class, true)) {
    // fix blueprint before using the directory
}

Type guard

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

Try / catch

try {
    $object = $directory->createObject($data, $key);
} catch (\RuntimeException $e) {
    if (str_starts_with($e->getMessage(), 'Bad object class')) {
        // configuration defect: log the class name and fail loudly
    }
}

Prevention

When it happens

Trigger: A custom flex type whose blueprint sets data.object to a misspelled class, a class in a non-autoloaded namespace, a plain data class not extending FlexObject, or a YAML quoting mistake that doubles the backslashes; loading any object from such a directory.

Common situations: First setup of a custom flex type in a plugin; moving/renaming plugin classes without updating the blueprint; disabling the plugin that provided the class while its blueprint remains cached.

Related errors


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