gastownhall/beads · error
committing migration %s: %w
Error message
committing migration %s: %w
What it means
This error wraps a failure from commitMigrationStep when commitEachStep is enabled on the embedded Dolt path (internal/storage/schema/schema.go:1690). After a migration's SQL and cursor row succeed, the step is committed atomically (staging newly-dirtied tables plus the cursor row and running DOLT_COMMIT); if that commit fails, the pass aborts. Deliberately, the 'done' progress line is only printed after this commit succeeds, so this error means the migration is applied-but-uncommitted in the working set.
Source
Thrown at internal/storage/schema/schema.go:1690
return count, fmt.Errorf("migration %s: %w", mf.name, err)
}
sum := sha256.Sum256(data)
contentHash := hex.EncodeToString(sum[:])
if _, err := db.ExecContext(ctx, "INSERT IGNORE INTO "+src.cursorTable+" (version, content_hash) VALUES (?, ?)", mf.version, contentHash); err != nil {
return count, fmt.Errorf("recording %s in %s: %w", mf.name, src.cursorTable, err)
}
count++
// commitEachStep's DOLT_ADD/DOLT_COMMIT is the expensive, fallible
// part of this step on the production embedded path. The "done" line
// (and its timing) must land after that commit succeeds, not before
// it: printing "done" and then hitting a commit error would show an
// operator a false completion, and timing that stopped before the
// commit would understate the step's real cost. A failed commit
// returns before either print statement below runs.
if commitEachStep {
if err := commitMigrationStep(ctx, db, src.cursorTable, mf.name, dirtyBeforeStep); err != nil {
return count, fmt.Errorf("committing migration %s: %w", mf.name, err)
}
}
fmt.Fprintf(stderr, " done (%.1fs)\n", time.Since(start).Seconds())
if migrateStepFaultHook != nil {
if err := migrateStepFaultHook(ctx, db, mf.version); err != nil {
return count, err
}
}
}
return count, nil
}
// migrateStepFaultHook is a test-only seam. When non-nil it runs at the end of
// each applied migration step (after the step's per-step commit on the
// production path); returning an error aborts the pass, emulating a
// crash/kill/timeout mid-migration so tests can prove the retry converges.
// Production leaves it nil.View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped inner error from DOLT_ADD/DOLT_COMMIT and address that specific Dolt error.
- Run `bd doctor` to inspect and repair the embedded Dolt database / working set state.
- Check disk space and that the .beads database files are writable; Dolt commits need space to write new commits.
- If the working set contains unexpected dirty tables, commit or discard them deliberately (backup first) so the per-step commit can proceed.
- Restore from backup and re-run migrations if the Dolt repository is corrupted.
Example fix
// before: migration applied but uncommitted; retry fails on dirty working set bd migrate up # committing migration 0104_xxx: table 'issues' has unexpected changes // after: diagnose and repair the embedded repo first bd doctor # resolve or discard the unexpected working-set changes (after backup) cp -rf .beads /tmp/beads-backup && bd doctor --fix bd migrate up
Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight: ensure a clean Dolt working set before migrating
if out, err := dirtyTables(ctx, db, true); err != nil || len(out) > 0 {
return fmt.Errorf("refusing to migrate: dirty working set %v — run bd doctor", out)
}
if free, _ := diskFree(".beads"); free < minFreeBytes {
return fmt.Errorf("insufficient disk space for Dolt commits")
} Try / catch
err := runMigrationsWithCommitEachStep(ctx, db)
for attempt := 1; attempt <= 3 && err != nil; attempt++ {
if !strings.Contains(err.Error(), "committing migration ") {
break // not a per-step commit failure; don't retry
}
if fixErr := bdDoctorFix(ctx); fixErr != nil {
return fmt.Errorf("cannot repair working set: %w", fixErr)
}
err = runMigrationsWithCommitEachStep(ctx, db) // retry converges per #4566
} Prevention
- Run `bd doctor` before and after major upgrades.
- Keep ample free disk space where the .beads Dolt repository lives.
- Never rsync/copy a live .beads directory while bd is running.
- Treat 'done' absence on stderr as the signal that the per-step commit failed and the pass aborted.
- Keep per-step commits enabled (commitEachStep) so retries converge instead of stranding uncommitted migrations.
When it happens
Trigger: commitMigrationStep fails for migration <name>: dirtyTables diff query errors, DOLT_ADD of a newly-dirtied table fails (e.g. unknown/corrupt table), or DOLT_COMMIT fails for reasons other than 'nothing to commit' (working-set conflicts, storage errors).
Common situations: Killed/locked embedded Dolt database leaving an inconsistent working set; an uncommitted pre-existing mutation on a table the migration touched that the pre-flight dirty guard did not catch; disk-full during Dolt commit; database file corruption after a crash.
Related errors
- committing migrations: %w
- committing migration step: %w
- failed to commit is_blocked repairs to Dolt: %w
- failed to commit dependency key repairs: %w
- failed to commit orphaned dependency removals: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/8b550f98075f7a25.
Report an issue: GitHub.