getgrav/grav · error · RuntimeException
Bad index class: %s
Error message
Bad index class: %s
What it means
FlexDirectory::createIndex() (line 612) creates index objects from the blueprint key data.index (default GenericIndex), checking is_a(..., FlexIndexInterface::class, true) before instantiating. A class name that is misspelled, missing, or not a FlexIndex implementation throws 'Bad index class'; indexes are created on every directory lookup, so the directory is effectively unusable until fixed.
Source
Thrown at system/src/Grav/Framework/Flex/FlexDirectory.php:612
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);
}
return $className::createFromArray($entries, $this, $keyField);
}
/**
* @return string
*/
public function getObjectClass(): string
{
if (!$this->objectClassName) {
$this->objectClassName = $this->getConfig('data.object', GenericObject::class);
}
return $this->objectClassName;
}
/**View on GitHub (pinned to 6040efed04)
Solutions
- Point data.index at an autoloaded class implementing Grav\Framework\Flex\Interfaces\FlexIndexInterface (normally extending Grav\Framework\Flex\FlexIndex).
- Run composer dump-autoload and clear Grav cache so blueprint/config overrides are re-read.
- Remove stale data.index overrides in user:// config/blueprints after upgrading the plugin that owns the type.
Example fix
# before (user config override) data: index: 'Vendor\Plugin\Flex\OldIndex' # renamed in v2 # after data: index: 'Vendor\Plugin\Flex\ThingsIndex'
Defensive patterns
Strategy: validation
Validate before calling
$class = $directory->getConfig('data.index', \Grav\Framework\Flex\GenericIndex::class);
if (!class_exists($class) || !is_a($class, \Grav\Framework\Flex\Interfaces\FlexIndexInterface::class, true)) {
// fix blueprint before calling getIndex()/createIndex()
} Type guard
function isValidFlexIndexClass(string $class): bool
{
return class_exists($class)
&& \is_a($class, \Grav\Framework\Flex\Interfaces\FlexIndexInterface::class, true);
} Try / catch
try {
$index = $directory->createIndex($entries);
} catch (\RuntimeException $e) {
if (str_starts_with($e->getMessage(), 'Bad index class')) {
// configuration defect: log the class name and stop
}
} Prevention
- Assert custom index classes extend FlexIndex during plugin boot.
- Remove stale data.index overrides in user:// config after plugin upgrades.
- Regenerate the autoloader when class names change.
When it happens
Trigger: A custom flex type with a custom data.index class that fails to autoload or does not extend FlexIndex; renaming the index class in an update without updating shipped blueprints; cached blueprint/config in user:// still referencing an old class after a plugin upgrade.
Common situations: Plugin upgrades that move index classes; user config overriding flex type settings with stale names; environments where composer autoload differs (missing regenerated autoload).
Related errors
- Bad object class: %s
- Bad collection 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/a312bce2250a55a4.
Report an issue: GitHub.