gastownhall/beads · error
scan config conflict: %w
Error message
scan config conflict: %w
What it means
Returned by configConflictsAreMemoryConvergent when rows.Scan fails while reading a dolt_conflicts_config conflict row into two sql.NullString values (our_key, their_key). The wrapper indicates the row shape returned by the Dolt engine did not match what the code expects — the scan is typed and will fail on NULL-incompatible targets, driver type conversion problems, or iteration misuse. The whole auto-resolution pre-check aborts so no conflict is auto-resolved on uncertain data.
Source
Thrown at internal/storage/versioncontrolops/mergesettle.go:673
// metadata makes). Any other config key in conflict — issue_prefix above all,
// whose stale-value sweep GH#2455 specifically guards against — is a real
// semantic conflict, so the whole config table is left for the operator.
//
// The key column is config's primary key, so a same-key conflict carries the
// identical key on both sides; an add/delete conflict leaves one side NULL. A
// row is convergent only if every key it presents is a memory key.
func configConflictsAreMemoryConvergent(ctx context.Context, db DBConn) (bool, error) {
rows, err := db.QueryContext(ctx, `
SELECT our_key, their_key FROM dolt_conflicts_config`)
if err != nil {
return false, fmt.Errorf("query config conflicts: %w", err)
}
defer rows.Close()
for rows.Next() {
var ourKey, theirKey sql.NullString
if err := rows.Scan(&ourKey, &theirKey); err != nil {
return false, fmt.Errorf("scan config conflict: %w", err)
}
for _, k := range []sql.NullString{ourKey, theirKey} {
if k.Valid && !strings.HasPrefix(k.String, memoryConfigKeyPrefix) {
return false, nil
}
}
}
return true, rows.Err()
}
// resolvedConfigConflictKeys returns the keys of the config rows currently in
// conflict, used only to name the kv.memory.* keys whose local value the
// --theirs auto-resolution is about to supersede. It must be called BEFORE
// DOLT_CONFLICTS_RESOLVE clears dolt_conflicts_config. config's primary key is
// `key`, so a same-key conflict carries the identical key on both sides; an
// add/delete conflict leaves one side NULL, so COALESCE picks whichever side has
// it.
func resolvedConfigConflictKeys(ctx context.Context, db DBConn) ([]string, error) {View on GitHub (pinned to 71377f2769)
Solutions
- Check the underlying wrapped error: if it's a column-count mismatch, the Dolt version's conflict-table schema differs — align the bd build with the installed Dolt engine version.
- Retry the operation after reconnecting; transient driver errors mid-iteration can poison the rows iterator.
- If using a custom driver/mocked DBConn in tests, make its rows return exactly two columns compatible with sql.NullString.
- As a workaround, skip auto-resolution and resolve config conflicts manually (dolt conflicts resolve on the config table).
- Run 'bd doctor' or equivalent to validate the embedded Dolt storage layer for schema corruption.
Defensive patterns
Strategy: validation
Validate before calling
// Validate the conflict-table shape before scanning:
cols, err := db.QueryContext(ctx, "SHOW COLUMNS FROM dolt_conflicts_config")
if err != nil {
return err
}
// expect exactly our_key and their_key (plus base columns) of string type;
// if the layout differs, skip auto-resolution and resolve manually. Try / catch
if err := TryAutoResolveMergeConflicts(ctx, db); err != nil {
var scanErr *sql.ScanError
if errors.As(err, &scanErr) {
// column/type mismatch: fall back to manual resolution
}
} Prevention
- Pin the Dolt engine version your bd build was compiled against.
- In tests, make mock DBConn rows return the exact column count/types the code scans into.
- Avoid scanning rows after a prior iteration error; always check rows.Err().
- After Dolt upgrades, run a smoke merge on a scratch clone before production repos.
- Resolve conflicts manually whenever the conflict-table schema looks unfamiliar.
When it happens
Trigger: TryAutoResolveMergeConflicts invokes configConflictsAreMemoryConvergent and the Dolt driver returns a row whose our_key/their_key columns cannot be scanned into sql.NullString — e.g. the driver returned a different column count (schema drift across Dolt versions), a column of an unconvertible type, or the caller kept scanning after rows was already in an error state.
Common situations: Dolt engine upgrade/downgrade changing the dolt_conflicts_config column layout; a custom or mocked DBConn driver returning rows with mismatched column counts; using a driver whose Rows.Scan doesn't support sql.NullString for the conflict-table column types; reading rows after a network interruption mid-iteration.
Related errors
- iter dependents: scan: %w
- iter issues: scan: %w
- failed to resolve %s conflicts: %w
- query config conflicts: %w
- scan schema_migrations conflict: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/ec84ac40ac11e544.
Report an issue: GitHub.