gastownhall/beads · error
query conflict for %s %s: %w
Error message
query conflict for %s %s: %w
What it means
loadConflictRow failed while querying dolt_conflicts_<table> for a single row by key, matching either our_<keyCol> or their_<keyCol>. This wraps the underlying query error, meaning the SELECT itself failed to execute — connection, permission, or server-side failure — not merely absence of the row.
Source
Thrown at internal/storage/versioncontrolops/conflicts.go:309
for i, c := range r.cols {
side, field, ok := splitConflictColumn(c)
if !ok || side != "their" || conflictMetaSuffixes[field] || field == keyCol {
continue
}
names = append(names, field)
vals = append(vals, r.vals[i])
}
return names, vals
}
// loadConflictRow fetches the single conflict row for key. It matches on
// either side's key column so a row only one side has is *found* and then
// refused with a precise message, rather than reported as "no conflict".
func loadConflictRow(ctx context.Context, db DBConn, table, keyCol, key string) (rawConflictRow, error) {
q := fmt.Sprintf("SELECT * FROM `dolt_conflicts_%s` WHERE `our_%s` = ? OR `their_%s` = ?", table, keyCol, keyCol) //nolint:gosec // identifiers validated
rows, err := db.QueryContext(ctx, q, key, key)
if err != nil {
return rawConflictRow{}, fmt.Errorf("query conflict for %s %s: %w", table, key, err)
}
defer func() { _ = rows.Close() }()
cols, err := rows.Columns()
if err != nil {
return rawConflictRow{}, fmt.Errorf("conflict columns for table %s: %w", table, err)
}
if !rows.Next() {
if err := rows.Err(); err != nil {
return rawConflictRow{}, fmt.Errorf("query conflict for %s %s: %w", table, key, err)
}
return rawConflictRow{}, fmt.Errorf("no live conflict for %s %s", table, key)
}
vals := make([]any, len(cols))
ptrs := make([]any, len(cols))
for i := range vals {
ptrs[i] = &vals[i]
}View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped error for root cause (timeout vs connection vs SQL error)
- Retry the key's resolution; per-row resolution is idempotent once already resolved
- Extend the context deadline when resolving many keys
- Ensure no other session is resolving the same conflicts concurrently
Example fix
// before
ctx := context.Background()
for _, key := range keys { ResolveConflictRows(ctx, db, table, []string{key}, strat) }
// after
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(len(keys))*500*time.Millisecond)
defer cancel() Defensive patterns
Strategy: retry
Try / catch
n, err := versioncontrolops.ResolveConflictRows(ctx, db, table, keys, strat)
if err != nil && strings.Contains(err.Error(), "query conflict for "+table) {
if isTransient(err) {
n, err = versioncontrolops.ResolveConflictRows(ctx, db, table, keys, strat) // idempotent retry
}
} Prevention
- Size the context deadline to the number of keys being resolved
- Prevent concurrent resolvers on the same conflicted table
- Keep the session's conflict-tolerance flags set for the whole loop
When it happens
Trigger: Called from ResolveConflictRows per key; the SELECT fails because the connection dropped, the context was cancelled, the session lost the conflict-tolerance flags, or the server rejected the query.
Common situations: Long per-key resolution loops exceeding context deadline; server restart mid-loop; the conflicted table was resolved concurrently by another session, invalidating conflict metadata.
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
- query conflicts for table %s: %w
- failed to begin transaction: %w
- failed to recompute is_blocked: %w
- failed to commit is_blocked repairs: %w
- failed to query orphaned dependencies: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/c28f8a44d1b7ab51.
Report an issue: GitHub.