gastownhall/beads · warning
conflict for issue %v was not cleared (no conflict row delet
Error message
conflict for issue %v was not cleared (no conflict row deleted)
What it means
After the UPDATE succeeded, the DELETE that clears dolt_conflicts_issues reported zero rows deleted (and no driver error). That means the conflict row this plan entry was built from no longer exists — typically because a concurrent process already resolved or removed it — so this code refuses to report success and aborts, since the plan is now stale.
Source
Thrown at internal/storage/versioncontrolops/automerge.go:627
// 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
}
// unionConflictsAreSafe reports whether every live conflict of a union-merged
// table (labels, comments, events) is the same row on both sides with matching
// columns — the only class where "union" has an unambiguous answer. A row
// missing on one side (a deletion racing an insert) or diverging columns in a
// supposedly immutable row goes to the operator.
func unionConflictsAreSafe(ctx context.Context, db DBConn, table string) ([]unionRowKey, bool, error) {
keyCols, ok := unionConflictKeyColumns[table]
if !ok {
return nil, false, fmt.Errorf("table %s is not union-mergeable", table)
}
rows, err := loadConflictRows(ctx, db, table)
if err != nil {
return nil, false, errView on GitHub (pinned to 71377f2769)
Solutions
- Re-run the merge: reload the conflict list fresh so the plan matches current dolt_conflicts_issues state.
- Check for concurrent `bd`/agent sessions on the same database and serialize them (only one resolver at a time).
- Verify how the conflict actually got cleared (parent queries / dolt history) — if another resolver settled it correctly, the re-run should simply find no conflicts.
- If it recurs with no visible competitor, check for hooks or cron jobs running `bd` sync concurrently.
Example fix
// before: two processes can both auto-resolve the same merge
TryAutoResolveMergeConflicts(ctx, db)
// after: take an advisory lock / single-flight guard before resolving
if err := acquireMergeLock(ctx, db); err != nil {
return fmt.Errorf("another resolver holds the merge lock: %w", err)
}
defer releaseMergeLock(ctx, db)
return TryAutoResolveMergeConflicts(ctx, db) Defensive patterns
Strategy: retry
Validate before calling
// snapshot conflict keys, resolve, and confirm state did not move underneath
rows, err := loadConflictKeys(ctx, db, "issues")
if err != nil {
return err
}
if len(rows) == 0 {
return nil // nothing to resolve — a competitor already cleared them
} Type guard
func isStaleConflictPlan(err error) bool {
return err != nil && strings.Contains(err.Error(), "no conflict row deleted")
} Try / catch
if err := TryAutoResolveMergeConflicts(ctx, db); err != nil {
if isStaleConflictPlan(err) {
// someone else resolved it; reload state and retry — second pass finds nothing
return TryAutoResolveMergeConflicts(ctx, db)
}
return err
} Prevention
- Take a merge/resolution lock so only one process auto-resolves at a time
- Do not manually run DOLT_CONFLICTS_RESOLVE while an agent is auto-merging
- Check for cron/CI jobs running `bd` sync on the same database concurrently
- Treat zero-deleted as 'someone else settled it' and re-check state rather than force-clearing
When it happens
Trigger: Between loadConflictRows and the DELETE, another session cleared the conflict (ran its own auto-resolve or DOLT_CONFLICTS_RESOLVE), or Dolt collapsed the conflict row after the working-set UPDATE made the sides identical; the plan's key no longer matches any row in dolt_conflicts_issues.
Common situations: Two `bd` sessions auto-resolving the same pull simultaneously; a scheduled sync job racing an interactive merge; dolt version where conflict rows are auto-pruned once cells match; user manually resolving conflicts in a SQL shell while an agent runs auto-merge.
Related errors
- merged values for issue %v matched no row (was it deleted co
- schema: verify fresh-bootstrap history: got %d commits, want
- schema: release migration lock: %w: returned %d
- apply merged values for issue %v: %w
- confirm issue %v still exists after writing merged values: %
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/2c37af6bdd20f7c5.
Report an issue: GitHub.