{"record":{"id":"48a7310a2229362c","repo":"gastownhall/beads","slug":"re-key-id-s-s-w","errorCode":null,"errorMessage":"re-key id %s -> %s: %w","messagePattern":"re-key id (.+?) -> (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/schema/aux_row_id_backfill.go","lineNumber":349,"sourceCode":"\t\t}\n\t\tsort.Strings(free)\n\t\ti := 0\n\t\tfor _, target := range targets {\n\t\t\tif held[target] {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttodo = append(todo, rekey{oldID: free[i], newID: target})\n\t\t\ti++\n\t\t}\n\t}\n\t// Deterministic UPDATE order (groups is a map) so runs are reproducible.\n\tsort.Slice(todo, func(i, j int) bool { return todo[i].oldID < todo[j].oldID })\n\n\tfor _, r := range todo {\n\t\t//nolint:gosec // G201: table name is a hardcoded constant, never user input.\n\t\tif _, err := db.ExecContext(ctx, fmt.Sprintf(`UPDATE %s SET id = ? WHERE id = ?`, t.name),\n\t\t\tr.newID, r.oldID); err != nil {\n\t\t\treturn true, fmt.Errorf(\"re-key id %s -> %s: %w\", r.oldID, r.newID, err)\n\t\t}\n\t}\n\treturn len(todo) > 0, nil\n}\n","sourceCodeStart":331,"sourceCodeEnd":354,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/aux_row_id_backfill.go#L331-L354","documentation":"This error wraps a failed per-row UPDATE that rewrites an auxiliary table's `id` from an old random ID to a new deterministic content-digest-derived ID during the aux row-ID backfill. It carries both the old and new IDs plus the underlying driver error (constraint violation, connection loss, etc.). Each UPDATE is a standalone statement, so a failure mid-pass leaves the table partially re-keyed; the sentinel mechanism allows resume on the next run.","triggerScenarios":"rekeyAuxRowTable executes `UPDATE <table> SET id = ? WHERE id = ?` for each pending row and the statement errors — duplicate-key conflict when the new deterministic ID already exists, connection drop, context cancellation, or the id column not being the expected CHAR(36) type.","commonSituations":"Digest-derived target id colliding with an existing row; migration racing concurrent writes to the aux table; database upgraded from a pre-0037 schema where `id` isn't CHAR(36); killed Dolt server mid-pass.","solutions":["Re-run MigrateUp: the pass is resumable via the in-progress sentinel and idempotent per digest group.","If duplicate-key errors repeat, inspect the aux table for rows already holding the target id and remove/merge stale duplicates before rerunning.","Confirm the table's `id` column is CHAR(36) (post-migration 0037); ensure base migrations complete first.","Avoid concurrent writers against the database while migrations run (use the migration lock path)."],"exampleFix":"// before: one bad row aborts the whole pass\nif _, err := db.ExecContext(ctx, fmt.Sprintf(`UPDATE %s SET id = ? WHERE id = ?`, t.name), r.newID, r.oldID); err != nil {\n\treturn true, fmt.Errorf(\"re-key id %s -> %s: %w\", r.oldID, r.newID, err)\n}\n// after: clear any row already occupying the deterministic target id first\ndb.ExecContext(ctx, fmt.Sprintf(`DELETE FROM %s WHERE id = ? AND id <> ?`, t.name), r.newID, r.oldID)\nif _, err := db.ExecContext(ctx, fmt.Sprintf(`UPDATE %s SET id = ? WHERE id = ?`, t.name), r.newID, r.oldID); err != nil {\n\treturn true, fmt.Errorf(\"re-key id %s -> %s: %w\", r.oldID, r.newID, err)\n}","handlingStrategy":"retry","validationCode":"// Verify the table and id column are in the expected state before migrating\nvar colType string\nerr := db.QueryRowContext(ctx,\n\t`SELECT DATA_TYPE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = 'id'`,\n\ttableName).Scan(&colType)\nif err == nil && colType != \"char\" {\n\treturn fmt.Errorf(\"table %s.id is %s, expected CHAR(36); run base migrations first\", tableName, colType)\n}","typeGuard":null,"tryCatchPattern":"err := migrateUp(ctx)\nif err != nil && strings.Contains(err.Error(), \"re-key id \") {\n\t// partial pass is resumable; serialize writes and retry\n\tdrainWriters()\n\terr = migrateUp(ctx)\n}","preventionTips":["Never allow concurrent writers to the DB while migrations run; use the migration lock","Ensure all numbered migrations (incl. 0037) complete before the aux rekey pass","Back up the database before upgrading so a mid-pass failure is recoverable","Re-run MigrateUp after failure — per-digest rekeying is idempotent"],"tags":["database","migration","sql","data-migration"],"backgroundTag":"row-rekey-update-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}