{"record":{"id":"6846779f9013c34f","repo":"gastownhall/beads","slug":"re-add-s-s-w","errorCode":null,"errorMessage":"re-add %s.%s: %w","messagePattern":"re-add (.+?)\\.(.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/doctor/fix/clone_local_fks.go","lineNumber":170,"sourceCode":"\t\t\t\tfk.Table, fk.Column, fk.RefTable, fk.RefColumn, fk.Table, fk.Column,\n\t\t\t)\n\t\t\tresult, err := db.Exec(deleteOrphans)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"delete %s orphans: %w\", fk.Table, err)\n\t\t\t}\n\t\t\tif verbose {\n\t\t\t\tremoved, _ := result.RowsAffected()\n\t\t\t\tfmt.Printf(\"  Removed %d orphaned row(s) from %s\\n\", removed, fk.Table)\n\t\t\t}\n\t\t}\n\n\t\t//nolint:gosec // G201: identifiers come from the fixed CloneLocalFKs spec, not user input.\n\t\taddConstraint := fmt.Sprintf(\n\t\t\t`ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s) ON DELETE CASCADE ON UPDATE CASCADE`,\n\t\t\tfk.Table, fk.Constraint, fk.Column, fk.RefTable, fk.RefColumn,\n\t\t)\n\t\tif _, err := db.Exec(addConstraint); err != nil {\n\t\t\treturn fmt.Errorf(\"re-add %s.%s: %w\", fk.Table, fk.Constraint, err)\n\t\t}\n\t\tfmt.Printf(\"  ✓ Re-linked %s.%s (%d orphaned row(s) removed)\\n\", fk.Table, fk.Constraint, fk.Orphans)\n\t}\n\treturn nil\n}\n","sourceCodeStart":152,"sourceCodeEnd":176,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/doctor/fix/clone_local_fks.go#L152-L176","documentation":"After removing orphaned rows, relinkSeveredCloneLocalFKs re-adds each missing foreign key with ALTER TABLE ... ADD CONSTRAINT. If the ALTER fails, this error wraps it, meaning the FK remains severed and future writes may not enforce referential integrity for clone-local references.","triggerScenarios":"ALTER TABLE ADD CONSTRAINT fails in relinkSeveredCloneLocalFKs (cmd/bd/doctor/fix/clone_local_fks.go:170) — remaining orphan rows the delete step missed, duplicate constraint name, ALTER privileges denied, or Dolt engine rejecting the FK definition.","commonSituations":"Races where new orphans were inserted between delete and re-add, running with a DB user lacking ALTER privileges, schema drift after manual migrations renaming tables/columns away from the CloneLocalFKs spec.","solutions":["Re-run the scan + fix (`bd doctor`) so orphans created in between are deleted before the constraint is re-added.","Check the wrapped error for 'duplicate constraint' — if so, the constraint already exists and the scan is stale; rescan.","Ensure the database user has ALTER privileges on the issues tables.","Verify the actual schema matches the CloneLocalFKs spec (table/column names) after any migration."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Confirm no orphans remain and the constraint name is free:\nvar orphans int\ndb.QueryRow(`SELECT COUNT(*) FROM issues t WHERE t.<fk> IS NOT NULL AND NOT EXISTS (...)`).Scan(&orphans)\nvar cons int\ndb.QueryRow(`SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_NAME = ?`, fk.Constraint).Scan(&cons)\n// orphans == 0 && cons == 0 means ALTER should succeed","typeGuard":null,"tryCatchPattern":"if err := CloneLocalFKEnforcement(ctx, db, verbose); err != nil && strings.Contains(err.Error(), \"re-add\") {\n\t// ALTER failed: rescan (frees races/stale state), then retry once\n\treturn err\n}","preventionTips":["Always run scan+delete+re-add as one doctor fix cycle, not manually","Ensure the DB user has ALTER privileges","Keep schema in sync with the CloneLocalFKs spec after migrations","Re-run doctor if the fix is interrupted mid-cycle"],"tags":["dolt","doctor","foreign-keys","alter-table"],"backgroundTag":"alter-table-add-constraint-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}