gastownhall/beads · error

checking dirty tables against pending migrations: %w

Error message

checking dirty tables against pending migrations: %w

What it means

This error wraps a failure in pendingMigrationDirtyTables, which cross-checks the set of pre-existing dirty (uncommitted) tables against the tables that pending migrations will alter. The check itself failed (as opposed to finding matches, which returns *DirtyTablesError instead). MigrateUp aborts because it cannot safely decide whether dirty tables would be clobbered.

Source

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

		return 0, fmt.Errorf("reading pre-migration status: %w", err)
	}
	delete(dirtyBefore, "dolt_ignore")
	// A previous pass that crashed mid-aux-rekey left its partial UPDATEs
	// dirty in the working set with the in-progress sentinel still recorded
	// (bd-578h9.16). Those tables are this pass's own migration state, not
	// pre-existing user writes: dropping them from dirtyBefore exempts them
	// from the changed-signature guard (the resumed rekey is about to change
	// them) and lets stageSchemaTables commit them with the rest of the pass.
	if resuming, err := anyAuxRekeyResumePending(ctx, db); err != nil {
		return 0, fmt.Errorf("reading aux rekey sentinel: %w", err)
	} else if resuming {
		for _, t := range auxRekeyTables {
			delete(dirtyBefore, t.name)
		}
	}
	touchedDirtyTables, err := mainSource.pendingMigrationDirtyTables(ctx, db, dirtyBefore)
	if err != nil {
		return 0, fmt.Errorf("checking dirty tables against pending migrations: %w", err)
	}
	if len(touchedDirtyTables) > 0 {
		return 0, &DirtyTablesError{Tables: touchedDirtyTables}
	}
	dirtyBeforeSignatures, err := dirtyTableSignatures(ctx, db, dirtyBefore)
	if err != nil {
		return 0, fmt.Errorf("reading pre-migration dirty table diffs: %w", err)
	}
	// Captured before the main migrations run: the aux re-key uses it to
	// distinguish the lineage's first rekey-aware migration (run the pass)
	// from a fresh clone of an already-converged lineage (record the marker
	// only, bd-578h9.4).
	mainVersionBefore, err := mainSource.currentVersion(ctx, db)
	if err != nil {
		return 0, fmt.Errorf("reading pre-migration schema version: %w", err)
	}

	applied, mainColumnAdded, err := mainSource.migrate(ctx, db, 0)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped cause for the failing query or bad migration metadata
  2. Fix or remove any migration file whose name lacks a numeric version prefix
  3. Ensure the database is opened read-write and the engine is healthy, then retry
  4. If only dirty-table detection is noisy, commit or stash unrelated working-set changes before migrating
Defensive patterns

Strategy: try-catch

Validate before calling

// Commit or verify a clean-enough working set before migrating
rows, _ := db.QueryContext(ctx, "SELECT table_name FROM dolt_status WHERE staged=0 AND committed=0")
// if suspicious user tables are dirty, resolve them before calling MigrateUp

Type guard

func isDirtyCheckErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "checking dirty tables against pending migrations")
}
var _ = isDirtyCheckErr

Try / catch

if _, err := schema.MigrateUp(ctx, db); err != nil {
    var dirty *schema.DirtyTablesError
    switch {
    case errors.As(err, &dirty):
        return fmt.Errorf("commit tables %v first", dirty.Tables)
    case isDirtyCheckErr(err):
        return fmt.Errorf("migration safety check failed: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling MigrateUp/MigrateUpWithLock when pendingMigrationDirtyTables returns an error - typically a Dolt query failure while enumerating pending migrations or dirty table names, or malformed migration metadata (unparseable version names).

Common situations: A migration file with a name that fails parseVersion, database opened read-only while the check attempts reads that fail, or transient embedded-Dolt engine errors during a busy working set.

Related errors


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