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

  1. Reload the table's schema metadata so originalForeignKeys come fresh from the DB catalog with constraint names populated.
  2. Check DB permissions — the user needs catalog access (e.g. information_schema/key_column_usage) for constraint names to be introspected.
  3. Never construct original FK objects client-side; treat hand-built FKs as new keys (addForeignKey) instead of originals.
  4. If developing: make the introspection query that fills fk_constraint_name mandatory, or treat nameless FKs as stillExists=true to skip the drop.
  5. 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

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


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/cb2b8f161df2e03b. Report an issue: GitHub.