octobercms/october · error · SystemException

Blueprint [get_class($blueprint)] does not use a database ta

Error message

Blueprint [get_class($blueprint)] does not use a database table.

What it means

SchemaBuilder::initBlueprint() prepares a content table for a blueprint, starting from $blueprint->getContentTableName(). When that method returns an empty value (blueprint kinds that store no content of their own, e.g. mixins), the builder has no table to migrate and throws a SystemException.

Source

Thrown at modules/tailor/classes/SchemaBuilder.php:134

    public static function migrateBlueprint(Blueprint $blueprint, Fieldset $fieldset)
    {
        $builder = new self;
        $builder->initBlueprint($blueprint, $fieldset);
        return $builder->migrate();
    }

    /**
     * initBlueprint
     */
    public function initBlueprint(Blueprint $blueprint, Fieldset $fieldset)
    {
        $this->fieldset = $fieldset;
        $this->blueprint = $blueprint;
        $this->tableName = $blueprint->getContentTableName();
        $this->tableBlueprint = $this->makeDatabaseBlueprint($fieldset);

        if (!$this->tableName) {
            throw new SystemException('Blueprint ['.get_class($blueprint).'] does not use a database table.');
        }

        $this->tableExists = Schema::hasTable($this->tableName);
        $this->tableColumns = $this->tableExists ? Schema::getColumnListing($this->tableName) : [];
    }

    /**
     * migrate
     */
    public function migrate()
    {
        $this->actionCount = 0;

        $this->migrateFields();
        $this->migrateJoins();
        $this->migrateRepeaters();
        $this->migrateTablePatches();

View on GitHub (pinned to b608633a7e)

Solutions

  1. Skip blueprints without a content table before building: `if (!$blueprint->getContentTableName()) continue;`
  2. Only schema-build content-bearing types (entry, stream, structure, single, global) and let Tailor's own refresh (`php artisan tailor:refresh`) handle the rest.

Example fix

// before
foreach ($blueprints as $blueprint) {
    $builder->initBlueprint($blueprint, $fieldset);
}

// after
foreach ($blueprints as $blueprint) {
    if (!$blueprint->getContentTableName()) {
        continue;
    }
    $builder->initBlueprint($blueprint, $fieldset);
}
Defensive patterns

Strategy: validation

Validate before calling

if (!$blueprint->getContentTableName()) {
    continue; // mixin-style blueprint: no table to build
}
$schemaBuilder->initBlueprint($blueprint, $fieldset);

Type guard

function isTableBackedBlueprint(\Tailor\Classes\Blueprint $blueprint): bool
{
    return (bool) $blueprint->getContentTableName();
}

Try / catch

try {
    $builder->initBlueprint($blueprint, $fieldset);
} catch (\SystemException $e) {
    // blueprint has no content table (e.g. mixin); skip it in custom tooling
}

Prevention

When it happens

Trigger: Passing a mixin blueprint (or any blueprint whose getContentTableName() is null) into SchemaBuilder::initBlueprint()/migrate() — typically from custom commands or scripts that iterate every blueprint from the indexer and schema-build each one indiscriminately.

Common situations: Custom migration/import tooling looping over all blueprints; new blueprint kinds introduced without content tables; code assuming every blueprint is content-bearing.

Related errors


AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21). Data as JSON: /api/errors/40fac18333d2c77f. Report an issue: GitHub.