{"record":{"id":"8b8af5e95b7a3360","repo":"Budibase/budibase","slug":"column-name-cannot-be-a-reserved-column-name","errorCode":null,"errorMessage":"Column name cannot be a reserved column name","messagePattern":"Column name cannot be a reserved column name","errorType":"validation","errorClass":"BadRequestError","httpStatus":400,"severity":"error","filePath":"packages/server/src/sdk/workspace/tables/migration.ts","lineNumber":41,"sourceCode":"export interface MigrationResult {\n  tablesUpdated: Table[]\n}\n\nexport async function migrate(\n  table: Table,\n  oldColumnName: string,\n  newColumnName: string\n): Promise<MigrationResult> {\n  if (newColumnName in table.schema) {\n    throw new BadRequestError(`Column \"${newColumnName}\" already exists`)\n  }\n\n  if (newColumnName === \"\") {\n    throw new BadRequestError(`Column name cannot be empty`)\n  }\n\n  if (isInternalColumnName(newColumnName)) {\n    throw new BadRequestError(`Column name cannot be a reserved column name`)\n  }\n\n  const oldColumn = table.schema[oldColumnName]\n\n  if (!oldColumn) {\n    throw new BadRequestError(\n      `Column \"${oldColumnName}\" does not exist on table \"${table.name}\"`\n    )\n  }\n\n  if (\n    oldColumn.type !== FieldType.LINK ||\n    oldColumn.tableId !== InternalTable.USER_METADATA\n  ) {\n    throw new BadRequestError(\n      `Only user relationship migration columns is currently supported`\n    )\n  }","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/tables/migration.ts#L23-L59","documentation":"migrate() rejects new column names that match internal/reserved column names via isInternalColumnName from shared-core; such names would collide with Budibase's system metadata columns and corrupt the schema.","triggerScenarios":"Calling migrate(table, oldColumnName, newName) where newName equals a reserved internal column name (e.g. table-level metadata keys checked by isInternalColumnName such as '_id'-style or reserved field identifiers).","commonSituations":"Users attempting to name a column like system fields; automation scripts deriving names from internal row fields; copy-pasting reserved names from docs.","solutions":["Pick a non-reserved name for the new column","Check isInternalColumnName(newName) from @budibase/shared-core before calling","Exclude reserved names from your UI's name suggestions/validation","Strip leading underscores or reserved prefixes from user input"],"exampleFix":"// before\nawait sdk.tables.migration.migrate(table, 'user', '_id')\n// after\nif (!isInternalColumnName('_id')) {\n  await sdk.tables.migration.migrate(table, 'user', '_id')\n}","handlingStrategy":"validation","validationCode":"import { isInternalColumnName } from '@budibase/shared-core'\nif (isInternalColumnName(newName)) {\n  throw new Error(`\"${newName}\" is a reserved column name`)\n}","typeGuard":"function isUsableColumnName(name: string): boolean {\n  return !isInternalColumnName(name)\n}","tryCatchPattern":"try {\n  await sdk.tables.migration.migrate(table, oldName, newName)\n} catch (e) {\n  if (e.message.includes('reserved column name')) {\n    // pick a non-reserved name\n  }\n}","preventionTips":["Import and use isInternalColumnName in your own validation","Disallow leading-underscore / system-style names in user inputs","Maintain a UI-side denylist mirroring reserved names"],"tags":["validation","reserved-names","migration"],"backgroundTag":"reserved-column-name","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}