Budibase/budibase · error · Error
A column type has changed.
Error message
A column type has changed.
What it means
save() compares the incoming table's column types against the existing table (fetched via opts.tableId) using hasTypeChanged; if any column's FieldType differs from the stored table, it refuses the save. This protects row data from being invalidated by in-place type changes.
Source
Thrown at packages/server/src/sdk/workspace/tables/internal/index.ts:88
opts?: {
userId?: string
tableId?: string
rowsToImport?: Row[]
renaming?: RenameColumn
isImport?: boolean
}
) {
const db = context.getWorkspaceDB()
// if the table obj had an _id then it will have been retrieved
let oldTable: Table | undefined
if (opts?.tableId) {
oldTable = await getTable(opts.tableId)
}
// check all types are correct
if (hasTypeChanged(table, oldTable)) {
throw new Error("A column type has changed.")
}
// check for case sensitivity - we don't want to allow duplicated columns
const duplicateColumn = findDuplicateInternalColumns(table)
if (duplicateColumn.length) {
throw new Error(
`Column(s) "${duplicateColumn.join(
", "
)}" are duplicated - check for other columns with these name (case in-sensitive)`
)
}
// check that subtypes have been maintained
table = checkAutoColumns(table, oldTable)
// saving a table is a complex operation, involving many different steps, this
// has been broken out into a utility to make it more obvious/easier to manipulate
const tableSaveFunctions = new TableSaveFunctions({View on GitHub (pinned to a81a902e9a)
Solutions
- Restore the original FieldType for the changed column and re-save
- Delete and recreate the column if the data can be dropped
- Use the supported column migration API (sdk.tables.migration) where one exists for the type transition
- Fetch the current table first and merge your changes onto its schema instead of sending a full replacement schema
Example fix
// before
table.schema.amount.type = FieldType.NUMBER
await sdk.tables.saveTable(table)
// after
// keep existing type, or drop/recreate the column deliberately
delete table.schema.amount
table.schema.amountNew = { name: 'amountNew', type: FieldType.NUMBER }
await sdk.tables.saveTable(table) Defensive patterns
Strategy: validation
Validate before calling
function assertTypesUnchanged(oldTable: Table, newTable: Table) {
for (const [name, col] of Object.entries(oldTable.schema)) {
if (newTable.schema[name] && newTable.schema[name].type !== col.type) {
throw new Error(`Column ${name} type changed`)
}
}
} Type guard
function isSameType(a: FieldSchema | undefined, b: FieldSchema | undefined): boolean {
return !!a && !!b && a.type === b.type
} Try / catch
try {
await sdk.tables.saveTable(table)
} catch (e) {
if (e.message === 'A column type has changed.') {
// re-fetch table, restore original types or drop/recreate the column
}
} Prevention
- Always fetch the current table and merge changes instead of sending a full schema
- Diff your schema against the stored one before saving
- Never change a column's type in place; use delete+create or a supported migration
- Watch for import/restore flows silently altering types
When it happens
Trigger: Calling sdk.tables.saveTable / save() (PUT /tables) with a schema in which an existing column has a different type field (e.g. changing a 'string' column to 'number', or a link to a bb-reference) without going through the proper migration path.
Common situations: Importing a table definition exported from another app where types differ; bulk-editing table JSON via the API; client-side code building schemas dynamically and defaulting types incorrectly.
Related errors
- Cannot make field "${key}" required, it has a default value.
- Column names can't contain special characters
- Project package dependency index is invalid.
- Column(s) "${duplicateColumn.join(", ")}" are duplicated - c
- Grouping by fields of type "${targetSchema.type}" is not sup
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/30ca78bde330f923.
Report an issue: GitHub.