octobercms/october · error · BlueprintException

Field name is reserved: {$fieldName}.

Error message

Field name is reserved: {$fieldName}.

What it means

Tailor reserves certain field names because they collide with columns, attributes and relations that generated content models always define — `attributes`, `site_id`, `site_root_id`, `created_user_id`, `relation_id`, `field_name`, `nest_left`, `nest_right`, `blueprint_uuid`, `primary_id`, `content_group`, `primaryRecord`, and more. The verifier rejects any field on that list (mirrored in SchemaBuilder::$reservedFieldNames), including fields arriving through imported fieldsets.

Source

Thrown at modules/tailor/classes/BlueprintVerifier.php:238

            }
        }

        // Validate source references from raw config (before fieldset expansion)
        $this->validateSourceReferences($blueprint, $fields);

        $fieldset = FieldManager::instance()->makeFieldset(['fields' => $fields]);
        $fieldset->validate();

        // Check invalid and reserved field names
        foreach ($fieldset->getAllFields() as $fieldName => $fieldObj) {
            if (!preg_match('/^[a-zA-Z0-9\_]+$/', $fieldName)) {
                $lineNo = $this->findLineFromKeyValPair($blueprint->content, $fieldName, '');
                throw new BlueprintException($blueprint, "Invalid field name: {$fieldName}.", $lineNo);
            }

            if (in_array($fieldName, $this->reservedFieldNames)) {
                $lineNo = $this->findLineFromKeyValPair($blueprint->content, $fieldName, '');
                throw new BlueprintException($blueprint, "Field name is reserved: {$fieldName}.", $lineNo);
            }
        }
    }

    /**
     * validateSourceReferences validates source references in raw field config recursively
     */
    protected function validateSourceReferences(Blueprint $blueprint, array $fields)
    {
        foreach ($fields as $fieldName => $fieldConfig) {
            if (!is_array($fieldConfig)) {
                continue;
            }

            // Check source at this level
            $source = $fieldConfig['source'] ?? null;
            if ($source && !$this->sourceExists($source)) {
                $lineNo = $this->findLineFromKeyValPair($blueprint->content, 'source', $source);

View on GitHub (pinned to b608633a7e)

Solutions

  1. Rename the field to a non-reserved name, e.g. `site` instead of `site_id`.
  2. Check the full reserved list in Tailor\Classes\BlueprintVerifier::{$reservedFieldNames} (duplicated in Tailor\Classes\SchemaBuilder) and avoid every entry.
  3. If the field comes from an imported fieldset, fix it in that fieldset file — the verifier checks the expanded field list.

Example fix

# before
fields:
  attributes:
    label: Attributes
    type: text

# after
fields:
  product_attributes:
    label: Attributes
    type: text
Defensive patterns

Strategy: validation

Validate before calling

$reserved = (new \ReflectionClass(\Tailor\Classes\BlueprintVerifier::class))
    ->getDefaultProperties()['reservedFieldNames'];
foreach ($fields as $name => $config) {
    if (in_array($name, $reserved)) {
        throw new InvalidArgumentException("Reserved field name: {$name}");
    }
}

Type guard

function isReservedTailorFieldName(string $name): bool
{
    static $reserved;
    $reserved = $reserved ?? (new \ReflectionClass(\Tailor\Classes\BlueprintVerifier::class))
        ->getDefaultProperties()['reservedFieldNames'];
    return in_array($name, $reserved, true);
}

Try / catch

try {
    // blueprint scan
} catch (\Tailor\Classes\BlueprintException $e) {
    // message names the reserved field; rename it in the blueprint or imported fieldset
}

Prevention

When it happens

Trigger: A blueprint field is named exactly `attributes`, `site_id`, `content_group`, `primaryRecord`, or another reserved entry; or a shared fieldset imported with `import:` contains one of those names and gets expanded by getAllFields().

Common situations: Naming a field after internal columns (e.g. `site_id` for a site picker, `attributes` for a generic fieldset); fieldsets written before a name became reserved in a newer October build; content_group used for navigation grouping.

Related errors


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