gastownhall/beads · error
apply merged values for issue %v: %w
Error message
apply merged values for issue %v: %w
What it means
This error wraps the driver/database error returned when an UPDATE that writes field-merged values over an issue's working-set row fails inside the dolt manual conflict-resolution path. resolveIssuesFieldMerge writes merged cells directly to the `issues` table (DOLT_CONFLICTS_RESOLVE cannot express per-cell merges), so any SQL failure — connection loss, schema mismatch, type error, lock timeout — is surfaced here tagged with the issue id. The conflict is left unresolved; the caller (TryAutoResolveMergeConflicts) aborts the auto-merge pass.
Source
Thrown at internal/storage/versioncontrolops/automerge.go:602
}
if len(m.columns) > 0 {
sets := make([]string, len(m.columns))
args := make([]any, 0, len(m.columns)+1)
for i, col := range m.columns {
// MySQL cannot bind an identifier and a peer's schema merge can
// extend the conflict table's columns, so gate every name the
// same way the table name is gated.
if err := ValidateConflictTable(col); err != nil {
return fmt.Errorf("refusing to write unexpected column %q of issues: %w", col, err)
}
sets[i] = fmt.Sprintf("`%s` = ?", col)
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)
}
}
}View on GitHub (pinned to 71377f2769)
Solutions
- Re-run `bd dolt pull`/merge after confirming the database is reachable and writable; the conflict row is untouched so the merge can be retried.
- Check the wrapped driver error (%w) for type-mismatch on a specific column; if a peer schema change added it, re-sync schemas and retry.
- Verify no other process holds a lock on the Dolt database (e.g. a stale server or second `bd` doing schema work) and retry.
- If the error repeats, resolve the conflict manually (DOLT_CONFLICTS_RESOLVE --ours/--theirs or editing the row) and re-run.
- Confirm the user the connection uses has UPDATE privilege on `issues` and the working set is not read-only.
Example fix
// before: plan built once, then applied without any schema/type check on values
args = append(args, m.values[i])
res, err := db.ExecContext(ctx, stmt, args...)
// after: coerce merged values through the same normalization used for comparison before binding
for i, col := range m.columns {
m.values[i] = conflictCellsNormalize(m.values[i])
}
res, err := db.ExecContext(ctx, stmt, args...) Defensive patterns
Strategy: try-catch
Validate before calling
// before triggering auto-merge, check connectivity and writability
var live int
if err := db.QueryRowContext(ctx, "SELECT 1").Err; err != nil {
return fmt.Errorf("database unreachable before merge: %w", err)
}
if _, err := db.ExecContext(ctx, "SELECT COUNT(*) FROM `issues` LIMIT 1"); err != nil {
return fmt.Errorf("issues table not readable/schema mismatch: %w", err)
} Type guard
func isDriverError(err error) bool {
var de interface{ Error() string; Unwrap() error }
return errors.As(err, &de) && strings.Contains(err.Error(), "apply merged values")
} Try / catch
if err := TryAutoResolveMergeConflicts(ctx, db); err != nil {
var wrapped interface{ Unwrap() error }
if errors.As(err, &wrapped) {
log.Printf("auto-merge failed for issue (driver: %v); conflict left unresolved — retry or resolve manually", errors.Unwrap(err))
}
// safe fallback: conflict row remains; a later pull can retry
return fallbackManualResolve(ctx, db)
} Prevention
- Keep the Dolt connection stable; run merges against a local/embedded database when the network is unreliable
- Apply peer schema migrations before merging so conflict-table columns match the working-set schema
- Ensure the merge user has UPDATE privileges on `issues` and the database is not read-only
- Avoid running merges concurrently with schema-changing commands
When it happens
Trigger: db.ExecContext on `UPDATE issues SET ... WHERE <key> = ?` fails during auto conflict resolution — e.g. the Dolt/MySQL connection dropped mid-merge, a column added on the peer branch has an incompatible type so a merged value cannot bind, the database is read-only/locked by another process, or the working-set schema diverged from the conflict-table schema the plan was built from.
Common situations: Two `bd` sessions merging concurrently while one connection is killed or times out; a peer schema migration added a column and the merged value's Go type (e.g. []byte vs int64) cannot be bound; running against a replica or read-only database; Dolt server restarted during a long merge.
Related errors
- ErrExec
- database not available: %w
- search gates: %w
- database not available: %w
- failed to get template: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/dc12c9959010a350.
Report an issue: GitHub.