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

  1. 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.
  2. Retry the operation after reconnecting; transient driver errors mid-iteration can poison the rows iterator.
  3. If using a custom driver/mocked DBConn in tests, make its rows return exactly two columns compatible with sql.NullString.
  4. As a workaround, skip auto-resolution and resolve config conflicts manually (dolt conflicts resolve on the config table).
  5. 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

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


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