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

  1. Only call migrate() on internal tables - filter with !isExternalTableID(table._id) before invoking
  2. Convert the external table data to an internal Budibase table first, then migrate
  3. 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

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


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/b292e5f7835fd007. Report an issue: GitHub.