gastownhall/beads · error

%s: %w

Error message

%s: %w

What it means

dirtyTableSignatures fingerprints each pre-existing dirty table by hashing the full dolt_diff('HEAD','WORKING') diff, so MigrateUp can later prove migrations did not touch user data. When computing a table's signature fails (dirtyTableSignature error — unsafe name, or dolt_diff query/scan failure), the error is wrapped with the table name: "<table>: %w" and MigrateUp aborts before running any migration.

Source

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

	}
	return nil
}

func unstageIgnoredTables(ctx context.Context, db DBConn) error {
	tables, err := existingIgnoredTables(ctx, db)
	if err != nil {
		return err
	}
	return unstagePreExistingTables(ctx, db, tables)
}

func dirtyTableSignatures(ctx context.Context, db DBConn, tables map[string]dirtyTableState) (map[string]string, error) {
	signatures := make(map[string]string, len(tables))
	names := sortedDirtyTableNames(tables)
	for _, table := range names {
		signature, err := dirtyTableSignature(ctx, db, table)
		if err != nil {
			return nil, fmt.Errorf("%s: %w", table, err)
		}
		signatures[table] = signature
	}
	return signatures, nil
}

func changedDirtyTableSignatures(ctx context.Context, db DBConn, before map[string]string) ([]string, error) {
	var changed []string
	names := sortedSignatureTableNames(before)
	for _, table := range names {
		signature, err := dirtyTableSignature(ctx, db, table)
		if err != nil {
			return nil, fmt.Errorf("%s: %w", table, err)
		}
		if signature != before[table] {
			changed = append(changed, table)
		}
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Identify the table named in the message and the wrapped cause; run `dolt status` and `dolt diff <table>` manually in the .beads database directory to reproduce.
  2. Stop concurrent writers (other `bd` instances, background sync) and retry — the table may have disappeared mid-scan.
  3. Commit or clean the dirty table's changes yourself (dolt add/commit or checkout) so it no longer appears dirty, then rerun `bd`.
  4. If dolt_diff itself errors, check the embedded Dolt version matches what beads expects and upgrade if mismatched.
  5. Inspect DirtyTablesError guidance (#4566): pre-existing dirty tables are allowed but must remain unchanged; stabilizing the working set clears this path.

Example fix

// before: MigrateUp fails while fingerprinting a dirty table
_, err := bd.Open(dbPath)
// after: settle the dirty table first via dolt, then open
//   cd .beads && dolt add issues && dolt commit -m "user data"
_, err = bd.Open(dbPath)
Defensive patterns

Strategy: validation

Validate before calling

// Go: verify every dirty table name is fingerprintable before migrating
rows, _ := db.QueryContext(ctx, "SELECT table_name FROM dolt_status WHERE dirty")
for rows.Next() {
    var name string
    _ = rows.Scan(&name)
    if !isSafeTableName(name) {
        return fmt.Errorf("rename table %q before migrating", name)
    }
}

Type guard

// Go: narrow per-table signature failures (format: "<table>: <cause>")
func isDirtyTableSignatureError(err error) (table string, ok bool) {
    if err == nil { return "", false }
    msg := err.Error()
    // heuristic: this family surfaces as "committing migrations" wrapper's cause
    if i := strings.Index(msg, ": "); i > 0 {
        return msg[:i], true
    }
    return "", false
}

Try / catch

err := schema.MigrateUp(ctx, db)
if err != nil {
    var dirtyErr *schema.DirtyTablesError
    if errors.As(err, &dirtyErr) {
        // pre-existing dirty tables: commit/reset them, then retry
    } else {
        // signature/diff failure: stop concurrent writers and retry
    }
}

Prevention

When it happens

Trigger: MigrateUp calls dirtyTableSignatures over committableDirtyTables output; a table's dolt_diff query errors (e.g. table dropped/renamed between status read and diff, query error from the Dolt engine, or row scan/type error), or dirtyTableSignature rejects the table name.

Common situations: Concurrent process mutates the schema (drops/renames tables) between dolt_status read and diff computation; a table in dolt_status with characters the dolt_diff call cannot handle; Dolt engine version returning an unexpected dolt_diff result shape; corrupted working set.

Related errors


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