{"record":{"id":"2fa71f4a4e4a3fb0","repo":"Budibase/budibase","slug":"cannot-duplicate-external-tables","errorCode":null,"errorMessage":"Cannot duplicate external tables","messagePattern":"Cannot duplicate external tables","errorType":"http","errorClass":"HTTPError","httpStatus":422,"severity":"warning","filePath":"packages/server/src/api/controllers/table/index.ts","lineNumber":326,"sourceCode":"  let tableId = ctx.params.tableId as string\n  const table = await sdk.tables.getTable(tableId)\n  let result = await sdk.tables.migrate(table, oldColumn, newColumn)\n\n  for (let table of result.tablesUpdated) {\n    builderSocket?.emitTableUpdate(ctx, table, {\n      includeOriginator: true,\n    })\n  }\n\n  ctx.body = { message: `Column ${oldColumn} migrated.` }\n}\n\nexport async function duplicate(ctx: UserCtx<void, SaveTableResponse>) {\n  const tableId = ctx.params.tableId as string\n  const table = await sdk.tables.getTable(tableId)\n\n  if (isExternalTable(table)) {\n    throw new HTTPError(\"Cannot duplicate external tables\", 422)\n  }\n\n  const duplicatedTable = await sdk.tables.duplicate(table, ctx.user._id)\n\n  ctx.message = `Table ${table.name} duplicated successfully.`\n  ctx.body = duplicatedTable\n\n  const processedTable = await processTable(duplicatedTable)\n  builderSocket?.emitTableUpdate(ctx, cloneDeep(processedTable))\n}\n\nexport async function publish(\n  ctx: UserCtx<PublishTableRequest, PublishTableResponse>\n) {\n  const tableId = ctx.params.tableId as string\n  const table = await sdk.tables.getTable(tableId)\n\n  if (!table) {","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/table/index.ts#L308-L344","documentation":"Table duplication is only supported for internal (CouchDB) tables. The duplicate endpoint explicitly rejects external tables (backed by SQL/REST datasources) with HTTP 422 because rows cannot be cloned through the external connector in the same way.","triggerScenarios":"POST to the table duplicate endpoint (/tables/:tableId/duplicate) where tableId resolves to a table whose datasource is external (isExternalTable true), e.g. a PostgreSQL/MySQL/REST datasource table.","commonSituations":"Users attempting to duplicate SQL-backed tables from the Builder data section; scripts iterating all tables and duplicating each without filtering by datasource type.","solutions":["Duplicate the table inside the source database directly and re-sync the datasource in Budibase","Create a new internal table and copy data via a Budibase automation/script","Use a query against the external datasource that performs the copy (e.g. SQL CREATE TABLE ... AS SELECT)"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"async function canDuplicate(tableId, api) {\n  const table = await api.getTable(tableId)\n  return table && table.source !== \"external\" && table.type !== \"external\"\n}","typeGuard":"const isInternalTable = (t) => Boolean(t) && !t.sourceId && t.source !== \"external\"","tryCatchPattern":"try {\n  await api.duplicateTable(tableId)\n} catch (e) {\n  if (e?.status === 422 && /Cannot duplicate external tables/.test(e.message)) {\n    // fall back to copying data into a new internal table via script/automation\n  } else throw e\n}","preventionTips":["Filter table lists by datasource type before bulk duplication","Educate users that SQL/REST tables cannot be duplicated in-app","Perform duplication at the database layer for external tables","Gate duplicate actions in custom UIs on datasource type"],"tags":["table","external-datasource","duplication"],"backgroundTag":"unsupported-operation-on-external-table","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}