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
- Point data.object at an autoloaded class implementing Grav\Framework\Flex\Interfaces\FlexObjectInterface — in practice, one extending Grav\Framework\Flex\FlexObject.
- 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.
- 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
- Add a boot-time assertion for custom flex types verifying data.object implements the interface.
- Run composer dump-autoload after moving plugin classes.
- Review YAML quoting of namespaced class names in blueprints.
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
- Bad collection class: %s
- Bad index class: %s
- Bad storage class: %s
- Unknown extension type
- Bad Data Formatter
AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17).
Data as JSON: /api/errors/67379d1a864ee3d6.
Report an issue: GitHub.