gastownhall/beads · error
confirm issue %v still exists after writing merged values: %
Error message
confirm issue %v still exists after writing merged values: %w
What it means
After the merged-values UPDATE reports zero RowsAffected (or RowsAffected itself errors), the code must distinguish 'row already had these values' from 'row vanished'. It re-queries via conflictTargetStillPresent; if that existence check itself fails, this error wraps the underlying query error. The conflict stays unresolved and the auto-merge aborts to avoid discarding a side undetectably.
Source
Thrown at internal/storage/versioncontrolops/automerge.go:614
args = append(args, m.values[i])
}
args = append(args, m.ourKey)
stmt := fmt.Sprintf("UPDATE `issues` SET %s WHERE `%s` = ?", strings.Join(sets, ", "), issuesKeyColumn) //nolint:gosec // identifiers validated above
res, err := db.ExecContext(ctx, stmt, args...)
if err != nil {
return fmt.Errorf("apply merged values for issue %v: %w", m.ourKey, err)
}
// Zero rows would mean the row we planned against is gone —
// another session deleted it between the read and the write, and
// clearing the conflict now would discard their side undetectably.
// But RowsAffected is rows CHANGED, not rows MATCHED: the DSN does
// not set clientFoundRows (doltutil/dsn.go), so a write the backend
// normalizes to the bytes already stored also reports zero. Only a
// follow-up existence check can tell "vanished" from "no-op".
if n, err := res.RowsAffected(); err != nil || n == 0 {
present, err := conflictTargetStillPresent(ctx, db, "issues", issuesKeyColumn, m.ourKey)
if err != nil {
return fmt.Errorf("confirm issue %v still exists after writing merged values: %w", m.ourKey, err)
}
if !present {
return fmt.Errorf("merged values for issue %v matched no row (was it deleted concurrently?); conflict left unresolved", m.ourKey)
}
}
}
res, err := db.ExecContext(ctx,
"DELETE FROM dolt_conflicts_issues WHERE our_"+issuesKeyColumn+" = ?", m.ourKey)
if err != nil {
return fmt.Errorf("clear conflict for issue %v: %w", m.ourKey, err)
}
if n, err := res.RowsAffected(); err == nil && n == 0 {
return fmt.Errorf("conflict for issue %v was not cleared (no conflict row deleted)", m.ourKey)
}
}
return nil
}
View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped %w driver error; if it is a timeout/cancellation, raise the context deadline or timeout for the merge command and retry.
- Confirm the Dolt server is up and stable (`bd doctor` / connection check), then re-run the merge; the conflict row is still present.
- If network is flaky, run the merge locally against the embedded database instead of a remote server, then push.
- Ensure the `issues` working-set table and its key column are queryable by the connection user (SELECT privilege).
Defensive patterns
Strategy: retry
Validate before calling
// pre-flight: ensure the connection survives a quick round-trip
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
if err := db.PingContext(ctx); err != nil {
return fmt.Errorf("database ping failed before merge: %w", err)
} Try / catch
err := TryAutoResolveMergeConflicts(ctx, db)
if err != nil && strings.Contains(err.Error(), "confirm issue") {
// transient existence-check failure: retry once after backoff
time.Sleep(2 * time.Second)
err = TryAutoResolveMergeConflicts(ctx, db)
}
return err Prevention
- Use generous context timeouts for merge operations (two sequential queries must both succeed)
- Check network stability to remote Dolt servers before long-running merges
- Avoid server restarts/maintenance windows during merge operations
- Prefer local embedded mode when the remote connection is flaky
When it happens
Trigger: RowsAffected returned 0 or errored after the merged-values UPDATE, and the follow-up SELECT in conflictTargetStillPresent(ctx, db, "issues", ...) failed — connection drop, the existence query hit a timeout, or the dolt_conflicts_issues/working-set metadata tables are unreadable at that moment.
Common situations: Unstable connection to a remote Dolt server (two queries in quick succession, second one drops); server restarting between the UPDATE and the existence check; transient network partition during merge; query cancellation because the caller's ctx deadline expired mid-merge.
Related errors
- apply merged values for issue %v: %w
- merged values for issue %v matched no row (was it deleted co
- clear conflict for issue %v: %w
- conflict for issue %v was not cleared (no conflict row delet
- ErrTransaction
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/793d2a38d92f6a63.
Report an issue: GitHub.