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

  1. Restore the connection/server and retry the merge auto-resolution
  2. Reduce conflict volume by merging smaller, more frequent increments
  3. 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

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


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