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

  1. Inspect the wrapped %w driver error; if it is a timeout/cancellation, raise the context deadline or timeout for the merge command and retry.
  2. Confirm the Dolt server is up and stable (`bd doctor` / connection check), then re-run the merge; the conflict row is still present.
  3. If network is flaky, run the merge locally against the embedded database instead of a remote server, then push.
  4. 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

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


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