strapi/strapi · error · Error
The attribute "${attributeName}" is reserved and cannot be u
Error message
The attribute "${attributeName}" is reserved and cannot be used in a model. Please rename "${contentType.modelName}" attribute "${attributeName}" to something else. What it means
Thrown during content-type-to-model transformation when a user-defined attribute snake-cases to 'document_id' or to the DB id column (identifiers.ID_COLUMN, conventionally 'id'). These names are reserved because Strapi auto-injects a documentId attribute and an auto-increment id column into every content type; allowing a user attribute with the same DB column name would cause a schema collision.
Source
Thrown at packages/core/core/src/utils/transform-content-types-to-models.ts:297
contentTypes.forEach((contentType) => {
assert(contentType.collectionName, 'Content type "collectionName" is required');
assert(contentType.modelName, 'Content type "modelName" is required');
assert(contentType.uid, 'Content type "uid" is required');
// Add document id to content types
// as it is not documented
const documentIdAttribute: Record<string, Schema.Attribute.AnyAttribute> =
contentType.modelType === 'contentType'
? { documentId: { type: 'string', default: createDocumentId } }
: {};
// TODO: this needs to be combined with getReservedNames, we should not be maintaining two lists
// Prevent user from creating a documentId attribute
const reservedAttributeNames = ['document_id', identifiers.ID_COLUMN];
Object.keys(contentType.attributes || {}).forEach((attributeName) => {
const snakeCasedAttributeName = _.snakeCase(attributeName);
if (reservedAttributeNames.includes(snakeCasedAttributeName)) {
throw new Error(
`The attribute "${attributeName}" is reserved and cannot be used in a model. Please rename "${contentType.modelName}" attribute "${attributeName}" to something else.`
);
}
});
if (hasComponentsOrDz(contentType)) {
const compoLinkModel = createCompoLinkModel(contentType, identifiers);
models.push(compoLinkModel);
}
const model: Model = {
uid: contentType.uid,
singularName: contentType.modelName,
tableName: contentType.collectionName, // This gets shortened in metadata.loadModels(), so we don't shorten here or it will happen twice
attributes: {
[identifiers.ID_COLUMN]: {
type: 'increments',
},View on GitHub (pinned to 4a4101264d)
Solutions
- Rename the offending attribute to something that does not snake-case to 'id' or 'document_id' (e.g. 'documentRef', 'externalId', 'customId').
- Search the content type's schema.json for keys named id, documentId, document_id and remove or rename them.
- After renaming, clear the rebuilt model cache and restart Strapi.
Example fix
// before — src/api/article/content-types/article/schema.json
{ "attributes": { "documentId": { "type": "string" } } }
// after
{ "attributes": { "externalDocumentId": { "type": "string" } } } Defensive patterns
Strategy: validation
Validate before calling
const RESERVED = ['id', 'document_id'];
const snake = (s) => s.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase();
const offenders = Object.keys(schema.attributes).filter((name) => RESERVED.includes(snake(name)));
if (offenders.length) {
throw new Error(`Reserved attribute names: ${offenders.join(', ')}`);
} Type guard
const isReservedAttributeName = (name: string): boolean => {
const snake = name.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase();
return ['id', 'document_id'].includes(snake);
}; Prevention
- Never name a content-type attribute 'id', 'documentId', or any casing that snake-cases to those.
- Add a pre-commit lint rule that scans schema.json files for reserved names.
- During v4→v5 migration, audit all content types for 'id'-like attributes.
When it happens
Trigger: Defining a content type whose schema.json attributes include 'documentId', 'document_id', 'id', 'ID', or any casing that snake-cases to 'document_id' or 'id'. Fires at Strapi boot during metadata loading, before the server starts.
Common situations: Migrating from Strapi v4 (where 'id' was the only identifier) to v5 (which adds documentId) and keeping a custom 'id'-like attribute; naming a field 'documentId' for a business concept; importing a content type schema from another system that uses those names.
Related errors
- Content Type Definition is invalid for ${uid}'.\n${e.errors}
- Could not load content type found at ${dir}
- You cannot convert a collectionType to a singleType when hav
- Content type ${attribute.target} not found
- contentType.notFound
AI-assisted analysis of strapi/strapi@4a4101264d (2026-08-12).
Data as JSON: /api/errors/da2e88d0277789b4.
Report an issue: GitHub.