{"record":{"id":"e45f1da940513673","repo":"gastownhall/beads","slug":"adding-s-for-the-0058-repair-w","errorCode":null,"errorMessage":"adding %s for the 0058 repair: %w","messagePattern":"adding (.+?) for the 0058 repair: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/schema/wisp_dep_forward_repair.go","lineNumber":464,"sourceCode":"\t}\n\treturn nil\n}\n\n// ensureWispDepFinalKeysAndConstraints completes the final shape. Each object\n// is added only if absent, so this finishes a partially-rebuilt table rather\n// than failing on a duplicate key name -- and re-running it on a converged\n// database does nothing at all.\nfunc ensureWispDepFinalKeysAndConstraints(ctx context.Context, db DBConn) error {\n\tfor _, k := range wispDepFinalKeys {\n\t\tpresent, err := schemaIndexExists(ctx, db, wispDepTable, k.name)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif present {\n\t\t\tcontinue\n\t\t}\n\t\tif _, err := db.ExecContext(ctx, \"ALTER TABLE wisp_dependencies \"+k.definition); err != nil {\n\t\t\treturn fmt.Errorf(\"adding %s for the 0058 repair: %w\", k.name, err)\n\t\t}\n\t}\n\n\tfor _, c := range wispDepFinalConstraints {\n\t\tpresent, err := schemaConstraintExists(ctx, db, wispDepTable, c.name)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif present {\n\t\t\tcontinue\n\t\t}\n\t\tif _, err := db.ExecContext(ctx,\n\t\t\t\"ALTER TABLE wisp_dependencies ADD CONSTRAINT \"+c.name+\" \"+c.definition); err != nil {\n\t\t\treturn fmt.Errorf(\"adding %s for the 0058 repair: %w\", c.name, err)\n\t\t}\n\t}\n\treturn nil\n}","sourceCodeStart":446,"sourceCodeEnd":482,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/wisp_dep_forward_repair.go#L446-L482","documentation":"Wraps a failure while adding one of the final key/index definitions (wispDepFinalKeys) to wisp_dependencies during the 0058 repair. Each key is added only if absent, so this fires when the ALTER TABLE ... ADD INDEX/KEY statement itself fails.","triggerScenarios":"During repairWispDependenciesForwardShape, ensureWispDepFinalKeysAndConstraints executes 'ALTER TABLE wisp_dependencies <key.definition>' for an absent key and the DDL fails — e.g. duplicate values still present that violate a UNIQUE key, insufficient privileges, or a Dolt/MySQL DDL limitation.","commonSituations":"Repairing a legacy store where dedupe was skipped or failed silently, leaving duplicate natural-identity rows so UNIQUE key creation fails; running under a restricted DB account; concurrent access holding metadata locks on the table.","solutions":["Run dedupeWispDepNaturalIdentity / the full repair first so duplicate rows are removed before UNIQUE keys are added","Check the wrapped error for 'Duplicate entry' and delete the offending duplicate rows manually, then re-run","Run the repair with a DB account that has ALTER/INDEX privileges","Retry when no other connections hold locks on wisp_dependencies"],"exampleFix":"-- before: UNIQUE key add fails\nALTER TABLE wisp_dependencies ADD UNIQUE KEY uk_wisp_dep_natural (...); -- Duplicate entry\n-- after\ndelete duplicate rows first (or re-run the repair's dedupe step), then re-run the repair","handlingStrategy":"retry","validationCode":"// pre-check duplicates that would break UNIQUE keys\nSELECT issue_id, depends_on_issue_id, depends_on_wisp_id, depends_on_external, COUNT(*) c\nFROM wisp_dependencies GROUP BY 1,2,3,4 HAVING c > 1;","typeGuard":null,"tryCatchPattern":"if err := ensureWispDepFinalKeysAndConstraints(ctx, db); err != nil {\n    if strings.Contains(err.Error(), \"Duplicate entry\") {\n        // re-run dedupe step, then retry once\n    }\n    return err\n}","preventionTips":["Run the dedupe step before adding UNIQUE keys","Check for 'Duplicate entry' in the wrapped error and clean rows before retrying","Avoid concurrent sessions during DDL repairs","Grant ALTER/INDEX privileges to the migration user"],"tags":["database","migration","schema-repair","ddl"],"backgroundTag":"schema-migration-failure","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}