{"record":{"id":"b106c33cc738bdba","repo":"gastownhall/beads","slug":"clearing-aux-rekey-sentinel-w","errorCode":null,"errorMessage":"clearing aux rekey sentinel: %w","messagePattern":"clearing aux rekey sentinel: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/storage/schema/aux_row_id_backfill.go","lineNumber":259,"sourceCode":"\t}\n\n\t// Sentinel before the first UPDATE: a crash anywhere in the rewrite\n\t// leaves it set, so the next pass resumes (the rewrite is idempotent)\n\t// instead of recording the marker over partially re-keyed rows.\n\tif err := setAuxRekeyInProgress(ctx, db, pass.sentinelKey); err != nil {\n\t\treturn false, fmt.Errorf(\"recording aux rekey sentinel: %w\", err)\n\t}\n\n\twrote := false\n\tfor _, t := range auxRekeyTables {\n\t\tw, err := rekeyAuxRowTable(ctx, db, t)\n\t\twrote = wrote || w\n\t\tif err != nil {\n\t\t\treturn wrote, fmt.Errorf(\"%s: %w\", t.name, err)\n\t\t}\n\t}\n\tif err := clearAuxRekeyInProgress(ctx, db, pass.sentinelKey); err != nil {\n\t\treturn wrote, fmt.Errorf(\"clearing aux rekey sentinel: %w\", err)\n\t}\n\treturn wrote, nil\n}\n\n// rekeyAuxRowTable re-derives the ids of one table. The whole table is grouped\n// by content digest; each digest's rows take the deterministic ids for\n// ordinals 0..n-1. A row already holding one of its group's target ids keeps\n// it (idempotence: re-running never swaps ids within a group), and the\n// remaining rows take the remaining targets in sorted-current-id order. Across\n// clones that assignment may permute within a group of exact-duplicate rows,\n// but duplicates are interchangeable and the id set is identical, so the\n// merged result still converges.\nfunc rekeyAuxRowTable(ctx context.Context, db DBConn, t auxRekeyTable) (bool, error) {\n\t// Skip cleanly if the table or its id column isn't present (older or partial\n\t// schema): nothing to re-key. After MigrateUp's main pass the id column is\n\t// CHAR(36) on any schema this runs against (0037 precedes the marker).\n\thasID, err := columnExists(ctx, db, t.name, \"id\")\n\tif err != nil {","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/aux_row_id_backfill.go#L241-L277","documentation":"This error wraps a failure to delete the 'aux rekey in progress' sentinel row from local_metadata after an auxiliary-table ID re-key pass completes. The rekey already finished; only the cleanup DELETE failed, so the sentinel remains and the next migration run will resume/re-run the pass. It indicates a write failure against the local_metadata table (connection loss, lock timeout, permissions, or cancellation) at the very end of a migration pass.","triggerScenarios":"rekeyAuxRowIDsPending calls clearAuxRekeyInProgress (DELETE FROM local_metadata WHERE key = <sentinelKey>) after the rekey pass succeeds; the DELETE fails due to a dropped/cancelled context, DB connection loss, insufficient privileges on local_metadata, or a lock conflict during migration.","commonSituations":"Network blip or Dolt server restart mid-migration; context cancelled by the user Ctrl-C'ing a long `bd` migration; local_metadata table locked by another session; running with a DB user lacking DELETE privilege.","solutions":["Re-run the migration (MigrateUp re-invokes the pass because the sentinel is still set; the rekey is idempotent and will converge and clear the sentinel this time).","Check DB connectivity and that the Dolt server is up, then retry.","Verify the DB user has DELETE privilege on local_metadata.","Check the context isn't being cancelled prematurely by the caller (timeouts, signal handling)."],"exampleFix":"// before: sentinel left behind after transient DELETE failure\nif err := clearAuxRekeyInProgress(ctx, db, pass.sentinelKey); err != nil {\n\treturn wrote, fmt.Errorf(\"clearing aux rekey sentinel: %w\", err)\n}\n// after: best-effort cleanup, sentinel is safe to resume next run\nif err := clearAuxRekeyInProgress(ctx, db, pass.sentinelKey); err != nil {\n\tlog.Printf(\"warning: clearing aux rekey sentinel %s: %v (will resume next run)\", pass.sentinelKey, err)\n}","handlingStrategy":"retry","validationCode":"// Before triggering migration, check the sentinel table is accessible\nvar n int\nif err := db.QueryRowContext(ctx, \"SELECT COUNT(*) FROM local_metadata\").Scan(&n); err != nil {\n\treturn fmt.Errorf(\"local_metadata not accessible: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"result, err := runMigration(ctx)\nif err != nil && strings.Contains(err.Error(), \"clearing aux rekey sentinel\") {\n\t// cleanup-only failure: re-run; the pass is idempotent and will clear the sentinel\n\tresult, err = runMigration(context.Background())\n}","preventionTips":["Don't cancel contexts mid-migration; use a background context for the migration phase","Ensure the DB user has full privileges on local_metadata","Run migrations while the Dolt server is stable (no restarts or maintenance windows)","Re-run migrations after any interruption — sentinel logic makes passes resumable"],"tags":["database","migration","cleanup","dolt"],"backgroundTag":"migration-sentinel-clear-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}