{"record":{"id":"f625c39d98c701f2","repo":"Budibase/budibase","slug":"column-s-duplicatecolumn-join-are-dupl","errorCode":null,"errorMessage":"Column(s) \"${duplicateColumn.join(\", \")}\" are duplicated - check for other columns with these name (case in-sensitive)","messagePattern":"Column\\(s\\) \"(.+?)\" are duplicated - check for other columns with these name \\(case in-sensitive\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/sdk/workspace/tables/internal/index.ts","lineNumber":94,"sourceCode":"  }\n) {\n  const db = context.getWorkspaceDB()\n\n  // if the table obj had an _id then it will have been retrieved\n  let oldTable: Table | undefined\n  if (opts?.tableId) {\n    oldTable = await getTable(opts.tableId)\n  }\n\n  // check all types are correct\n  if (hasTypeChanged(table, oldTable)) {\n    throw new Error(\"A column type has changed.\")\n  }\n\n  // check for case sensitivity - we don't want to allow duplicated columns\n  const duplicateColumn = findDuplicateInternalColumns(table)\n  if (duplicateColumn.length) {\n    throw new Error(\n      `Column(s) \"${duplicateColumn.join(\n        \", \"\n      )}\" are duplicated - check for other columns with these name (case in-sensitive)`\n    )\n  }\n\n  // check that subtypes have been maintained\n  table = checkAutoColumns(table, oldTable)\n\n  // saving a table is a complex operation, involving many different steps, this\n  // has been broken out into a utility to make it more obvious/easier to manipulate\n  const tableSaveFunctions = new TableSaveFunctions({\n    userId: opts?.userId,\n    oldTable,\n    importRows: opts?.rowsToImport,\n  })\n  table = await tableSaveFunctions.before(table)\n","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/tables/internal/index.ts#L76-L112","documentation":"Internal tables store column names case-insensitively (SQLite keys are lowercased), so save() runs findDuplicateInternalColumns and rejects a schema where two columns differ only by case. The duplicated names are listed in the message.","triggerScenarios":"Saving a table whose schema contains e.g. 'Email' and 'email', or any two columns that collide after case-insensitive normalization, via sdk.tables.saveTable/save().","commonSituations":"Importing CSVs/Excel sheets with headers differing only in case; merging schemas from two tables; programmatic schema generation that doesn't dedupe.","solutions":["Rename one of the duplicated columns so all names are unique case-insensitively","Normalize incoming import headers to lowercase before creating columns","Check the listed names in the message and remove the redundant column","Deduplicate schema keys programmatically before calling save"],"exampleFix":"// before\nschema: { Email: {...}, email: {...} }\n// after\nschema: { email: {...} }","handlingStrategy":"validation","validationCode":"function findCaseDuplicates(schema: Table['schema']): string[] {\n  const seen = new Set<string>()\n  const dups = new Set<string>()\n  for (const name of Object.keys(schema)) {\n    const lc = name.toLowerCase()\n    if (seen.has(lc)) dups.add(name)\n    seen.add(lc)\n  }\n  return [...dups]\n}","typeGuard":null,"tryCatchPattern":"try {\n  await sdk.tables.saveTable(table)\n} catch (e) {\n  if (e.message.includes('are duplicated')) {\n    // parse names from message and rename/remove one of them\n  }\n}","preventionTips":["Lowercase/normalize all column names before saving or importing","Run a case-insensitive uniqueness check on schema keys before save","Normalize CSV/Excel headers during import","Deduplicate after merging schemas from multiple tables"],"tags":["schema","validation","case-sensitivity"],"backgroundTag":"duplicate-column-names","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}