gastownhall/beads · error
iterate conflicts for table %s: %w
Error message
iterate conflicts for table %s: %w
What it means
After iterating rows.Next() over dolt_conflicts_<table>, loadConflictRows checks rows.Err(); this wraps any iteration-level driver error. It catches errors that surfaced mid-iteration (e.g. connection dropped while streaming rows) which would otherwise be silently missed, ensuring a partial conflict read can never be treated as complete.
Source
Thrown at internal/storage/versioncontrolops/automerge.go:121
cols, err := rows.Columns()
if err != nil {
return nil, fmt.Errorf("conflict columns for table %s: %w", table, err)
}
var out []rawConflictRow
for rows.Next() {
vals := make([]any, len(cols))
ptrs := make([]any, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}
if err := rows.Scan(ptrs...); err != nil {
return nil, fmt.Errorf("scan conflict row for table %s: %w", table, err)
}
out = append(out, rawConflictRow{cols: cols, vals: vals})
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate conflicts for table %s: %w", table, err)
}
return out, nil
}
// duplicateConflictKey reports the first our-side key held by more than one
// live conflict row. Both resolvers settle a row by deleting its conflict BY
// KEY, so two rows sharing one key would both be cleared by the first delete
// and make the second iteration abort on "no conflict row deleted" — a message
// about the wrong thing entirely, after a row was resolved without ever being
// merged. loadConflictRow refuses the same shape on the operator's single-row
// path (conflicts.go); the auto-merge pre-screens instead DECLINE on it, which
// is this file's idiom and what lets the caller still build the
// MergeConflictsError that tells an operator which tables need them.
//
// Rows whose our-side key is absent are skipped: a delete/modify conflict NULLs
// our whole side, such a row is never the target of a keyed delete (the safety
// checks decline it first), and treating several of them as one repeated key
// would decline merges that are perfectly settleable.View on GitHub (pinned to 71377f2769)
Solutions
- Restore the connection/server and retry the merge auto-resolution
- Reduce conflict volume by merging smaller, more frequent increments
- Increase server query timeouts for large conflict sets
Defensive patterns
Strategy: retry
Validate before calling
if err := db.PingContext(ctx); err != nil {
return fmt.Errorf("connection unhealthy before conflict resolution: %w", err)
} Try / catch
err := versioncontrolops.TryAutoResolveMergeConflicts(ctx, db)
if err != nil && strings.Contains(err.Error(), "iterate conflicts for table") {
// stream broke mid-iteration: reconnect, re-enter merge state if
// needed, and retry the resolution from scratch
return retryAfterReconnect(ctx, db)
} Prevention
- Resolve conflicts promptly after the merge, before connections idle out
- Increase server query timeouts for large conflict sets
- Merge in smaller increments to keep conflict sets small
- Use wired/stable networking for remote Dolt servers during merges
When it happens
Trigger: Auto-merge resolution streams conflict rows and the connection or server fails mid-iteration — network drop, server restart, or query killed while reading dolt_conflicts rows.
Common situations: Remote Dolt server over flaky network; long-running resolution interrupted by server maintenance or timeout; OOM/killed query on huge conflict sets.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- failed to remove backup: %w
- server not reachable: %w
- dolt server connection failed: %w
- dolt sql-server is not running on %s:%d; start it with 'bd d
- failed to ping server: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/9156e88baffe4d48.
Report an issue: GitHub.