{"record":{"id":"9c3f64fa55000e0a","repo":"gastownhall/beads","slug":"dependencies-w","errorCode":null,"errorMessage":"dependencies: %w","messagePattern":"dependencies: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/schema/dep_id_backfill.go","lineNumber":32,"sourceCode":"//\n// Migration 0043 minted dependencies.id from DEFAULT (UUID()), which is\n// per-clone-random; migration 0050 + the deterministic insert paths fix new\n// rows, but rows that already exist on an upgrading clone still carry the random\n// id. Leaving them would keep two independently-migrated clones divergent (same\n// edge, different primary key) and break `bd dolt pull`. This rewrites them to\n// the deterministic value so two clones converge to byte-identical dependencies.\n//\n// It runs from MigrateUp right after the schema migrations (so 0050 has already\n// asserted the canonical schema), and only on a pass where migration work was\n// needed — it is not part of the steady-state open path. It is idempotent: a row\n// already keyed deterministically is skipped, so re-running on a later migration\n// pass is a cheap no-op. dependencies changes are staged and committed by\n// MigrateUp; wisp_dependencies is dolt-ignored, so its re-key stays clone-local\n// (it only escapes on promotion, which copies the id).\nfunc rekeyDependencyIDs(ctx context.Context, db DBConn) (bool, error) {\n\twroteDeps, err := rekeyDependencyTable(ctx, db, \"dependencies\")\n\tif err != nil {\n\t\treturn wroteDeps, fmt.Errorf(\"dependencies: %w\", err)\n\t}\n\twroteWisp, err := rekeyDependencyTable(ctx, db, \"wisp_dependencies\")\n\tif err != nil {\n\t\treturn wroteDeps || wroteWisp, fmt.Errorf(\"wisp_dependencies: %w\", err)\n\t}\n\treturn wroteDeps || wroteWisp, nil\n}\n\n// rekeyDependencyTable re-derives ids for one edge table. table must be a\n// hardcoded constant (\"dependencies\" or \"wisp_dependencies\").\nfunc rekeyDependencyTable(ctx context.Context, db DBConn, table string) (bool, error) {\n\t// Skip cleanly if the table or its id column isn't present (e.g. an older or\n\t// partial schema where the surrogate key was never added): nothing to re-key.\n\thasID, err := columnExists(ctx, db, table, \"id\")\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif !hasID {","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/dep_id_backfill.go#L14-L50","documentation":"rekeyDependencyIDs rewrites dependencies.id values to deterministic depid.New(issue_id, target) values so independently-migrated clones converge and `bd dolt pull` works. This error wraps any failure from the re-key pass over the dependencies table specifically (the wisp_dependencies variant has its own wrapper).","triggerScenarios":"rekeyDependencyTable(ctx, db, \"dependencies\") fails — columnExists probe error, the SELECT of (id, issue_id, COALESCE(...)) fails, row scan failure, rows.Err(), or an UPDATE during the re-key loop errors.","commonSituations":"Upgrading an old clone whose dependencies table has pre-0050 UUID ids; connection drop mid-migration; a malformed row or unique-key conflict when two edges map to the same deterministic id; schema drift where the id column layout differs.","solutions":["Inspect the wrapped inner error (from the SELECT/UPDATE inside rekeyDependencyTable) for the root cause","Check for unique-key violations: duplicate (issue_id, target) edges collide on the same deterministic id — dedupe them","Re-run the migration pass — the re-key is idempotent and resumes","If the schema predates migration 0043/0050, run the full migration path (normal MigrateUp) rather than partial copies"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"-- detect rows needing rekey and duplicates before migrating\nSELECT issue_id, COALESCE(depends_on_issue_id, depends_on_wisp_id, depends_on_external) t, COUNT(*) c\nFROM dependencies GROUP BY issue_id, t HAVING c > 1;","typeGuard":null,"tryCatchPattern":"if _, err := rekeyDependencyIDs(ctx, db); err != nil {\n    if dberrors.IsUniqueViolation(err) {\n        return fmt.Errorf(\"duplicate dependency edges; dedupe before rekey: %w\", err)\n    }\n    return err\n}","preventionTips":["Run full MigrateUp rather than copying tables between clones partially","Ensure migration 0050 schema is applied before data backfills","Backup the database before upgrading old clones"],"tags":["go","dolt","migration","dependencies","backfill"],"backgroundTag":"dependency-id-rekey-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}