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

  1. Point data.index at an autoloaded class implementing Grav\Framework\Flex\Interfaces\FlexIndexInterface (normally extending Grav\Framework\Flex\FlexIndex).
  2. Run composer dump-autoload and clear Grav cache so blueprint/config overrides are re-read.
  3. 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

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


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