Budibase/budibase · error · BadRequestError
External tables cannot be migrated
Error message
External tables cannot be migrated
What it means
Budibase table column migrations (converting a user relationship column to a user BB_REFERENCE column) are only supported for internal (CouchDB) tables. getColumnMigrator checks the table ID and throws this BadRequestError when the table belongs to an external datasource (SQL/REST etc.). The migration SDK simply cannot rewrite rows/links for external tables.
Source
Thrown at packages/server/src/sdk/workspace/tables/migration.ts:106
throw e
}
}
interface ColumnMigrator {
doMigration(): Promise<MigrationResult>
}
function getColumnMigrator(
table: Table,
oldColumn: FieldSchema,
newColumn: FieldSchema
): ColumnMigrator {
// For now, we're only supporting migrations of user relationships to user
// columns in internal tables. In the future, we may want to support other
// migrations but for now return an error if we aren't migrating a user
// relationship.
if (isExternalTableID(table._id!)) {
throw new BadRequestError("External tables cannot be migrated")
}
if (!(oldColumn.name in table.schema)) {
throw new BadRequestError(`Column "${oldColumn.name}" does not exist`)
}
if (
newColumn.type !== FieldType.BB_REFERENCE_SINGLE &&
newColumn.type !== FieldType.BB_REFERENCE
) {
throw new BadRequestError(`Column "${newColumn.name}" is not a user column`)
}
if (newColumn.subtype !== BBReferenceFieldSubType.USER) {
throw new BadRequestError(`Column "${newColumn.name}" is not a user column`)
}
if (!isRelationshipField(oldColumn)) {View on GitHub (pinned to a81a902e9a)
Solutions
- Only call migrate() on internal tables - filter with !isExternalTableID(table._id) before invoking
- Convert the external table data to an internal Budibase table first, then migrate
- If the column must change on an external table, alter it directly in the source database and update the datasource schema in Budibase instead of using the migration SDK
Example fix
// before
await sdk.tables.migrate(table, "users", "userRef")
// after
import { isExternalTableID } from "../../../integrations/utils"
if (!isExternalTableID(table._id!)) {
await sdk.tables.migrate(table, "users", "userRef")
} Defensive patterns
Strategy: validation
Validate before calling
import { isExternalTableID } from "../integrations/utils"
if (isExternalTableID(table._id!)) {
throw new Error(`Table ${table._id} is external; migration not supported`)
} Type guard
const isInternalTable = (t: Table): boolean => !!t._id && !isExternalTableID(t._id)
Prevention
- Check table._id with isExternalTableID before any schema migration
- Only surface the migration UI/action for internal tables in your app code
When it happens
Trigger: Calling sdk.tables.migrate (or the migrate() API endpoint) with a table whose _id is an external table ID (contains a datasource prefix like datasources/...), i.e. a table backed by PostgreSQL/MySQL/REST rather than the internal database.
Common situations: Developers building automation or scripts that batch-convert relationship columns across all tables in an app without filtering out externally-connected tables; apps where the same schema code path serves internal and SQL tables.
Related errors
- Import data or url is required
- Unsupported import type
- Config id not found
- Slack app configuration token is required
- Slack app configuration refresh token is required
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/b292e5f7835fd007.
Report an issue: GitHub.