gastownhall/beads · error
iterate conflicts for table
Error message
iterate conflicts for table %s: %w
What it means
GetConflictRows scanned all visible rows but rows.Err() returned an error during final iteration — meaning the row iterator hit a failure mid-stream (connection loss, server error, context cancellation) rather than completing normally. This is checked after the loop so partially collected data is discarded and the iteration failure is reported.
Solutions
- Inspect the wrapped error for cancellation vs connection loss
- Increase the context timeout or re-run with a longer deadline
- Retry on a fresh connection; conflicts data is read-only so retry is safe
- Check dolt sql-server logs for query kills or restarts
Example fix
// before ctx := context.Background() // after ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel()
Defensive patterns
Strategy: retry
Try / catch
var rows []storage.ConflictRow
var err error
for attempt := 0; attempt < 3; attempt++ {
rows, err = versioncontrolops.GetConflictRows(ctx, db, table)
if err == nil || !isTransient(err) { break }
time.Sleep(backoff(attempt))
} Prevention
- Set generous context timeouts sized to table size
- Read conflicts on a dedicated healthy connection
- Treat iteration errors as safe-to-retry (read-only op)
When it happens
Trigger: The dolt connection drops or the query is aborted while GetConflictRows is iterating rows.Next(); context deadline exceeded mid-iteration; server-side kill of the running query.
Common situations: Long-running conflict scans over large conflicted tables hitting a context timeout; network instability between client and dolt sql-server; server restart during read.
Understand the failure class
Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.
Related errors
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/6890605393cc8e0c.
Report an issue: GitHub.
Appendix: source
Thrown at internal/storage/versioncontrolops/conflicts.go:144
cols, err := rows.Columns()
if err != nil {
return nil, fmt.Errorf("conflict columns for table %s: %w", table, err)
}
var out []storage.ConflictRow
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, buildConflictRow(table, cols, vals))
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("iterate conflicts for table %s: %w", table, err)
}
return out, nil
}
// buildConflictRow re-presents one raw conflict row as fields. It is pure, so
// the column-splitting rules are unit-testable without a database.
func buildConflictRow(table string, cols []string, vals []any) storage.ConflictRow {
row := storage.ConflictRow{Table: table}
index := make(map[string]int, len(cols))
for i, col := range cols {
side, field, ok := splitConflictColumn(col)
if !ok {
continue
}
v := formatConflictValue(vals[i])
if conflictMetaSuffixes[field] {
if field == "diff_type" && v != nil {
switch side {View on GitHub (pinned to 71377f2769)