getgrav/grav · error · RuntimeException

Bad index class

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.

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.

Appendix: 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)