{"record":{"id":"5ff68831e53566bd","repo":"Budibase/budibase","slug":"unable-to-generate-many-link-schema-noprimaryn","errorCode":null,"errorMessage":"Unable to generate many link schema, \"${noPrimaryName}\" does not have a primary key","messagePattern":"Unable to generate many link schema, \"(.+?)\" does not have a primary key","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/server/src/sdk/workspace/tables/external/utils.ts","lineNumber":88,"sourceCode":"\nexport function otherRelationshipType(type: RelationshipType) {\n  if (type === RelationshipType.MANY_TO_MANY) {\n    return RelationshipType.MANY_TO_MANY\n  }\n  return type === RelationshipType.ONE_TO_MANY\n    ? RelationshipType.MANY_TO_ONE\n    : RelationshipType.ONE_TO_MANY\n}\n\nexport function generateManyLinkSchema(\n  datasource: Datasource,\n  column: ManyToManyRelationshipFieldMetadata,\n  table: Table,\n  relatedTable: Table\n): Table {\n  if (!table.primary || !relatedTable.primary) {\n    const noPrimaryName = !table.primary ? table.name : relatedTable.name\n    throw new Error(\n      `Unable to generate many link schema, \"${noPrimaryName}\" does not have a primary key`\n    )\n  }\n  const primary = table.name + table.primary[0]\n  const relatedPrimary = relatedTable.name + relatedTable.primary[0]\n  const jcTblName = generateJunctionTableName(column, table, relatedTable)\n  const datasourceId = datasource._id!\n  // first create the new table\n  const junctionTable: Table = {\n    type: \"table\",\n    _id: buildExternalTableId(datasourceId, jcTblName),\n    name: jcTblName,\n    primary: [primary, relatedPrimary],\n    constrained: [primary, relatedPrimary],\n    sourceId: datasourceId,\n    sourceType: TableSourceType.EXTERNAL,\n    schema: {\n      [primary]: foreignKeyStructure(primary, {","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/sdk/workspace/tables/external/utils.ts#L70-L106","documentation":"generateManyLinkSchema builds junction tables for many-to-many relationships, which requires primary keys on both tables to compose the join columns. If either table lacks a primary key definition, it throws naming the offending table.","triggerScenarios":"Saving a many-to-many relationship where table.primary or relatedTable.primary is undefined/empty — typically external SQL tables synced without a detected primary key.","commonSituations":"Imported SQL tables without primary keys; views or tables where the connector failed to detect the PK; schema sync after a PK was dropped in the database.","solutions":["Add a primary key to the affected table in the connected SQL database and re-sync the datasource","Set table.primary in the Budibase table schema before creating the relationship","Use a table with a defined primary key for the many-to-many relationship","Refresh datasource entities so the connector re-detects keys"],"exampleFix":"// before\ntable.primary === undefined\n// after\ntable.primary = [\"id\"]\nawait tables.external.save(datasourceId, table)","handlingStrategy":"validation","validationCode":"if (!table.primary?.length || !relatedTable.primary?.length) {\n  throw new Error(\"Both tables need a primary key for many-to-many links\")\n}","typeGuard":"function hasPrimaryKey(t: Table): t is Table & { primary: string[] } {\n  return Array.isArray(t.primary) && t.primary.length > 0\n}","tryCatchPattern":"try {\n  await tables.external.save(datasourceId, table)\n} catch (err) {\n  if (err.message.includes(\"does not have a primary key\")) {\n    // add a PK in the source DB and re-sync before retrying\n  }\n}","preventionTips":["Ensure every external table has a database primary key","Re-sync datasource after altering keys in the source DB","Check table.primary after sync before creating relationships"],"tags":["external-datasource","many-to-many","missing-primary-key"],"backgroundTag":"missing-primary-key","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}