{"record":{"id":"cb2b8f161df2e03b","repo":"windmill-labs/windmill","slug":"original-foreign-key-missing-constraint-name-j","errorCode":null,"errorMessage":"Original foreign key missing constraint name : ${JSON.stringify(originalFk)}","messagePattern":"Original foreign key missing constraint name : (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/apps/components/display/dbtable/queries/alterTable.ts","lineNumber":388,"sourceCode":"\n\t// Check for renamed table.\n\tif (original.name !== updated.name) {\n\t\toperations.push({ kind: 'renameTable', to: updated.name })\n\t}\n\n\t// Check for foreign key changes\n\tconst originalForeignKeys = original.foreignKeys ?? []\n\tconst updatedForeignKeys = updated.foreignKeys ?? []\n\n\t// Check for dropped foreign keys\n\tfor (let i = 0; i < originalForeignKeys.length; i++) {\n\t\tconst originalFk = originalForeignKeys[i]\n\t\tconst stillExists = updatedForeignKeys.some((updFk) =>\n\t\t\tfkEqual(originalFk, normalizeNewFkToOldColNames(updFk, updated))\n\t\t)\n\t\tconst fk_constraint_name = originalFk.fk_constraint_name\n\t\tif (!fk_constraint_name) {\n\t\t\tthrow new Error(\n\t\t\t\t'Original foreign key missing constraint name : ' + JSON.stringify(originalFk)\n\t\t\t)\n\t\t}\n\t\tif (!stillExists) {\n\t\t\toperations.push({ kind: 'dropForeignKey', fk_constraint_name })\n\t\t}\n\t}\n\n\t// Check for added foreign keys\n\tfor (const updatedFk of updatedForeignKeys) {\n\t\tconst isNew = !originalForeignKeys.some((origFk) =>\n\t\t\tfkEqual(origFk, normalizeNewFkToOldColNames(updatedFk, updated))\n\t\t)\n\t\tif (isNew) {\n\t\t\toperations.push({ kind: 'addForeignKey', foreignKey: updatedFk })\n\t\t}\n\t}\n","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/apps/components/display/dbtable/queries/alterTable.ts#L370-L406","documentation":"diffTableEditorValues() compares original vs updated foreign keys to build drop/add operations. Dropping a FK requires its database constraint name; when an original foreign key has no fk_constraint_name (empty/undefined), it throws 'Original foreign key missing constraint name : {...}' with the offending object serialized.","triggerScenarios":"The table editor diff runs over originalForeignKeys loaded from the database where at least one FK lacks fk_constraint_name — typically when introspection metadata failed to fetch constraint names, or a foreign key object was constructed client-side (hand-built state, partially-loaded editor state) instead of coming from the DB catalog.","commonSituations":"Editing an existing table whose FK metadata wasn't fully loaded (permission-restricted catalog, introspection query returning partial rows); state restored from a saved app draft without constraint names; older saved data predating constraint-name capture.","solutions":["Reload the table's schema metadata so originalForeignKeys come fresh from the DB catalog with constraint names populated.","Check DB permissions — the user needs catalog access (e.g. information_schema/key_column_usage) for constraint names to be introspected.","Never construct original FK objects client-side; treat hand-built FKs as new keys (addForeignKey) instead of originals.","If developing: make the introspection query that fills fk_constraint_name mandatory, or treat nameless FKs as stillExists=true to skip the drop.","Inspect the JSON in the error message to identify which FK is missing its name and where it came from."],"exampleFix":"// before\nif (!fk_constraint_name) {\n  throw new Error('Original foreign key missing constraint name : ' + JSON.stringify(originalFk))\n}\n// after\nif (!fk_constraint_name) {\n  // can't issue a targeted drop without the DB constraint name; leave it untouched\n  continue\n}","handlingStrategy":"validation","validationCode":"const nameless = originalForeignKeys.filter(fk => !fk.fk_constraint_name)\nif (nameless.length) {\n  // refresh schema metadata before diffing, or skip drops for these FKs\n}","typeGuard":"function hasConstraintName(fk: TableEditorForeignKey): fk is TableEditorForeignKey & { fk_constraint_name: string } {\n  return typeof fk.fk_constraint_name === 'string' && fk.fk_constraint_name.length > 0\n}","tryCatchPattern":"try {\n  const ops = diffTableEditorValues(original, updated)\n} catch (e) {\n  if (String(e).startsWith('Original foreign key missing constraint name')) {\n    await reloadSchemaMetadata() // then retry the diff\n  } else throw e\n}","preventionTips":["Always load originalForeignKeys from live DB introspection, never from hand-built or stale saved state.","Verify introspection returns fk_constraint_name (check catalog permissions) before enabling FK editing.","Re-fetch schema metadata when the editor state is older than the last introspection."],"tags":["frontend","sql","dbtable","foreign-keys","introspection"],"backgroundTag":"missing-constraint-name","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}