gastownhall/beads · error

unstaging pre-migration tables: %w

Error message

unstaging pre-migration tables: %w

What it means

This error wraps a failure of `unstagePreExistingTables` — before migrating, MigrateUp unstages any pre-existing dirty tables (after exempting pass-owned dolt_ignore) so migration steps commit atomically. Failing here aborts before the first migration step, protecting the clean-working-set contract.

Source

Thrown at internal/storage/schema/schema.go:603

		if seedChanged {
			if err := commitSeededDoltIgnore(ctx, db); err != nil {
				return 0, err
			}
		}
		return 0, nil
	}

	dirtyBeforeAll, err := dirtyTables(ctx, db, false)
	if err != nil {
		return 0, fmt.Errorf("reading pre-migration status: %w", err)
	}
	// dolt_ignore is pass-owned state: seedDoltIgnorePatterns above may have
	// just dirtied it on an under-seeded database. Exempting it from the
	// pre-existing-dirty guards (like the aux-rekey tables below) keeps the
	// pending-migration and changed-signature gates off it.
	delete(dirtyBeforeAll, "dolt_ignore")
	if err := unstagePreExistingTables(ctx, db, dirtyBeforeAll); err != nil {
		return 0, fmt.Errorf("unstaging pre-migration tables: %w", err)
	}
	// The seed must not ride the migration pass: every step of the pass
	// commits atomically, and a pass killed between steps must leave a CLEAN
	// working set (the #4566 self-heal contract) — but the seeded dolt_ignore
	// rows would stay dirty until the pass's final commit, so an interrupted
	// retry sees a dirty working set and refuses to converge. Commit the seed
	// scoped and labeled now, after pre-existing staged tables were unstaged
	// (so nothing else rides into the commit) and before the first step runs.
	if seedChanged {
		if err := commitSeededDoltIgnore(ctx, db); err != nil {
			return 0, err
		}
	}
	dirtyBefore, err := committableDirtyTables(ctx, db)
	if err != nil {
		return 0, fmt.Errorf("reading pre-migration status: %w", err)
	}
	delete(dirtyBefore, "dolt_ignore")

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped cause; run `dolt status` to see what is staged
  2. Manually unstage or commit the pre-existing changes (`dolt reset` / `dolt commit`)
  3. Ensure dolt_ignore is not the blocker — it is exempted automatically; other staged tables are not
  4. Retry MigrateUp once the working set is clean

Example fix

// before: user-staged tables block unstaging
$ dolt status // staged: my_table
$ bd migrate // fails unstaging pre-migration tables
// after: commit or reset the staged change first
$ dolt commit -m "user change"
$ bd migrate
Defensive patterns

Strategy: validation

Validate before calling

// ensure no foreign staged tables before migrating
rows, err := db.QueryContext(ctx, "CALL DOLT_STATUS()")
for rows.Next() {
	var table, state string
	_ = rows.Scan(&table, &state)
	if table != "dolt_ignore" {
		return fmt.Errorf("table %s is staged; commit or reset first", table)
	}
}

Try / catch

_, err := schema.MigrateUp(ctx, db)
if err != nil && strings.Contains(err.Error(), "unstaging pre-migration tables") {
	// manually commit/reset staged user tables, then retry
	return fmt.Errorf("resolve staged tables manually: %w", err)
}

Prevention

When it happens

Trigger: MigrateUp calling unstagePreExistingTables for dirtyBeforeAll tables when a DOLT_RESET/unstage call fails — conflicts in the working set, a Dolt server rejecting procedure calls, or a read-only store.

Common situations: Pre-existing staged changes from a crashed prior process that cannot be reset; Dolt server in a conflicted state; running migrations against a database whose working set contains user changes staged outside bd.

Related errors


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