{"record":{"id":"a3625a7d71c2d0ec","repo":"gastownhall/beads","slug":"recording-aux-rekey-sentinel-w","errorCode":null,"errorMessage":"recording aux rekey sentinel: %w","messagePattern":"recording aux rekey sentinel: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/schema/aux_row_id_backfill.go","lineNumber":247,"sourceCode":"\t\treturn false, nil\n\t}\n\tresume, err := auxRekeyResumePending(ctx, db, pass.sentinelKey)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"reading aux rekey sentinel: %w\", err)\n\t}\n\t// The fresh-clone skip must not fire on a lineage whose previous pass\n\t// crashed mid-rekey: that pass already advanced the main cursor past the\n\t// shipped version, but its sentinel proves the rewrite never finished\n\t// (bd-578h9.16).\n\tif mainVersionBefore >= pass.shippedMainVersion && !resume {\n\t\treturn false, nil\n\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","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/aux_row_id_backfill.go#L229-L265","documentation":"Before the first UPDATE of the rekey rewrite, rekeyAuxRowIDsPending records an in-progress sentinel in local_metadata (setAuxRekeyInProgress) so a crash mid-rewrite leaves proof to resume on the next pass instead of recording the completion marker over partially re-keyed rows. This error wraps failure to write that sentinel (the CREATE TABLE IF NOT EXISTS plus REPLACE INTO), and aborts the pass before any table is rewritten.","triggerScenarios":"The pass is about to run (marker pending, resume decision made) and the sentinel writes fail: CREATE TABLE fails (no DDL privilege, read-only, conflict) or REPLACE INTO fails (INSERT privilege missing, table corrupt, connection drop).","commonSituations":"Migration user with read/write on data tables but not DDL or write on local_metadata; a fresh clone where local_metadata creation hits a privilege wall; disk-full or replication lag on the Dolt backend.","solutions":["Unwrap to the driver error; if it is the CREATE step see 'ensuring local_metadata', if REPLACE, grant INSERT/UPDATE on local_metadata.","Retry MigrateUp — nothing was rewritten, so the pass starts cleanly.","Verify local_metadata exists and is writable (bd doctor / EnsureIgnoredTables).","Check Dolt backend health: disk space, replication status, and open transactions."],"exampleFix":"-- before\n-- migration user lacks write access to local_metadata\n-- after\nGRANT INSERT, UPDATE, DELETE, CREATE ON mydb.* TO 'beads'@'%';\nFLUSH PRIVILEGES;","handlingStrategy":"retry","validationCode":"var canWrite int\n_ = db.QueryRow(`SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES\n WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'local_metadata' AND PRIVILEGE_TYPE = 'INSERT'`).Scan(&canWrite)\nif canCreateTableFails || canWrite == 0 { return errors.New(\"cannot write rekey sentinel to local_metadata\") }","typeGuard":null,"tryCatchPattern":"if err := migrateUp(ctx, db); err != nil {\n    if strings.Contains(err.Error(), \"recording aux rekey sentinel\") {\n        // sentinel write failed BEFORE any table rewrite; retry is clean\n        return retryWithBackoff(func() error { return migrateUp(ctx, db) })\n    }\n    return err\n}","preventionTips":["Grant the migration user INSERT/UPDATE/DELETE on local_metadata plus CREATE on the schema.","Verify disk space and Dolt replication health before long migrations.","Do not mark the pass complete manually — without the sentinel, a crash mid-rewrite would leave ambiguous state.","Re-run MigrateUp after fixing privileges; no tables were touched when this error fired."],"tags":["database","migration","dolt","crash-recovery"],"backgroundTag":"migration-sentinel-write-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}