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

  1. Read the wrapped inner error from DOLT_ADD/DOLT_COMMIT and address that specific Dolt error.
  2. Run `bd doctor` to inspect and repair the embedded Dolt database / working set state.
  3. Check disk space and that the .beads database files are writable; Dolt commits need space to write new commits.
  4. If the working set contains unexpected dirty tables, commit or discard them deliberately (backup first) so the per-step commit can proceed.
  5. 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

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


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