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
- 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.
- Stop concurrent writers (other `bd` instances, background sync) and retry — the table may have disappeared mid-scan.
- Commit or clean the dirty table's changes yourself (dolt add/commit or checkout) so it no longer appears dirty, then rerun `bd`.
- If dolt_diff itself errors, check the embedded Dolt version matches what beads expects and upgrade if mismatched.
- 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
- Avoid dropping/renaming tables in the beads DB while bd is starting
- Commit or clean dirty tables before running bd so the fingerprint pass is a no-op
- Run one bd process at a time against a given database
- Keep the embedded Dolt engine at the version beads expects
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
- failed to migrate credential keys: %w
- failed to update encrypted password for peer %s: %w
- failed to initialize schema: %w
- failed to rebuild pool after migration: %w
- ensuring local_metadata: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/dc0ca8edc194115c.
Report an issue: GitHub.