windmill-labs/windmill · error
Original foreign key missing constraint name : ${JSON.string
Error message
Original foreign key missing constraint name : ${JSON.stringify(originalFk)} What it means
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.
Source
Thrown at frontend/src/lib/components/apps/components/display/dbtable/queries/alterTable.ts:388
// Check for renamed table.
if (original.name !== updated.name) {
operations.push({ kind: 'renameTable', to: updated.name })
}
// Check for foreign key changes
const originalForeignKeys = original.foreignKeys ?? []
const updatedForeignKeys = updated.foreignKeys ?? []
// Check for dropped foreign keys
for (let i = 0; i < originalForeignKeys.length; i++) {
const originalFk = originalForeignKeys[i]
const stillExists = updatedForeignKeys.some((updFk) =>
fkEqual(originalFk, normalizeNewFkToOldColNames(updFk, updated))
)
const fk_constraint_name = originalFk.fk_constraint_name
if (!fk_constraint_name) {
throw new Error(
'Original foreign key missing constraint name : ' + JSON.stringify(originalFk)
)
}
if (!stillExists) {
operations.push({ kind: 'dropForeignKey', fk_constraint_name })
}
}
// Check for added foreign keys
for (const updatedFk of updatedForeignKeys) {
const isNew = !originalForeignKeys.some((origFk) =>
fkEqual(origFk, normalizeNewFkToOldColNames(updatedFk, updated))
)
if (isNew) {
operations.push({ kind: 'addForeignKey', foreignKey: updatedFk })
}
}
View on GitHub (pinned to e474e8803c)
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.
Example fix
// before
if (!fk_constraint_name) {
throw new Error('Original foreign key missing constraint name : ' + JSON.stringify(originalFk))
}
// after
if (!fk_constraint_name) {
// can't issue a targeted drop without the DB constraint name; leave it untouched
continue
} Defensive patterns
Strategy: validation
Validate before calling
const nameless = originalForeignKeys.filter(fk => !fk.fk_constraint_name)
if (nameless.length) {
// refresh schema metadata before diffing, or skip drops for these FKs
} Type guard
function hasConstraintName(fk: TableEditorForeignKey): fk is TableEditorForeignKey & { fk_constraint_name: string } {
return typeof fk.fk_constraint_name === 'string' && fk.fk_constraint_name.length > 0
} Try / catch
try {
const ops = diffTableEditorValues(original, updated)
} catch (e) {
if (String(e).startsWith('Original foreign key missing constraint name')) {
await reloadSchemaMetadata() // then retry the diff
} else throw e
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Unimplemented case
- Unsupported database type: ${dbType}
- Unsupported database type
- Unsupported database type
- Unsupported database type: ${dbType}
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/cb2b8f161df2e03b.
Report an issue: GitHub.