gastownhall/beads · error

recording aux rekey sentinel: %w

Error message

recording aux rekey sentinel: %w

What it means

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.

Source

Thrown at internal/storage/schema/aux_row_id_backfill.go:247

		return false, nil
	}
	resume, err := auxRekeyResumePending(ctx, db, pass.sentinelKey)
	if err != nil {
		return false, fmt.Errorf("reading aux rekey sentinel: %w", err)
	}
	// The fresh-clone skip must not fire on a lineage whose previous pass
	// crashed mid-rekey: that pass already advanced the main cursor past the
	// shipped version, but its sentinel proves the rewrite never finished
	// (bd-578h9.16).
	if mainVersionBefore >= pass.shippedMainVersion && !resume {
		return false, nil
	}

	// Sentinel before the first UPDATE: a crash anywhere in the rewrite
	// leaves it set, so the next pass resumes (the rewrite is idempotent)
	// instead of recording the marker over partially re-keyed rows.
	if err := setAuxRekeyInProgress(ctx, db, pass.sentinelKey); err != nil {
		return false, fmt.Errorf("recording aux rekey sentinel: %w", err)
	}

	wrote := false
	for _, t := range auxRekeyTables {
		w, err := rekeyAuxRowTable(ctx, db, t)
		wrote = wrote || w
		if err != nil {
			return wrote, fmt.Errorf("%s: %w", t.name, err)
		}
	}
	if err := clearAuxRekeyInProgress(ctx, db, pass.sentinelKey); err != nil {
		return wrote, fmt.Errorf("clearing aux rekey sentinel: %w", err)
	}
	return wrote, nil
}

// rekeyAuxRowTable re-derives the ids of one table. The whole table is grouped
// by content digest; each digest's rows take the deterministic ids for

View on GitHub (pinned to 71377f2769)

Solutions

  1. Unwrap to the driver error; if it is the CREATE step see 'ensuring local_metadata', if REPLACE, grant INSERT/UPDATE on local_metadata.
  2. Retry MigrateUp — nothing was rewritten, so the pass starts cleanly.
  3. Verify local_metadata exists and is writable (bd doctor / EnsureIgnoredTables).
  4. Check Dolt backend health: disk space, replication status, and open transactions.

Example fix

-- before
-- migration user lacks write access to local_metadata
-- after
GRANT INSERT, UPDATE, DELETE, CREATE ON mydb.* TO 'beads'@'%';
FLUSH PRIVILEGES;
Defensive patterns

Strategy: retry

Validate before calling

var canWrite int
_ = db.QueryRow(`SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_PRIVILEGES
 WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'local_metadata' AND PRIVILEGE_TYPE = 'INSERT'`).Scan(&canWrite)
if canCreateTableFails || canWrite == 0 { return errors.New("cannot write rekey sentinel to local_metadata") }

Try / catch

if err := migrateUp(ctx, db); err != nil {
    if strings.Contains(err.Error(), "recording aux rekey sentinel") {
        // sentinel write failed BEFORE any table rewrite; retry is clean
        return retryWithBackoff(func() error { return migrateUp(ctx, db) })
    }
    return err
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/a3625a7d71c2d0ec. Report an issue: GitHub.