gastownhall/beads · error
merge branch %s: %w (resolve with: bd vc merge %s --strategy
Error message
merge branch %s: %w (resolve with: bd vc merge %s --strategy ours|theirs)
What it means
Merge wraps Dolt's autocommit rejection of a conflicted merge (Error 1105: "@autocommit must be disabled so that merge conflicts can be resolved...") with actionable advice: "merge branch <branch>: <err> (resolve with: bd vc merge <branch> --strategy ours|theirs)". The library throws this shape when DOLT_MERGE ran under autocommit on a plain session, hit real conflicts, dolt_conflicts could not be inspected (or was empty), and isMergeConflictAutocommitError matched the error. Because the bare Merge cannot resolve conflicts itself, the message routes the operator to the strategy-based path.
Source
Thrown at internal/storage/versioncontrolops/version_control.go:115
//
// This runs as a bare DOLT_MERGE under autocommit, so a real conflict makes
// Dolt reject the implicit transaction (Error 1105: "@autocommit must be
// disabled so that merge conflicts can be resolved ...") before dolt_conflicts
// can even be inspected — conflicts = error here, same as plain `dolt merge`
// with no further flags. Callers that want the flag Dolt's error names —
// resolve-then-commit on conflict — must use MergeWithStrategy instead, which
// runs the merge on a pinned session with the conflict-tolerant flags set
// (#4992).
func Merge(ctx context.Context, db DBConn, branch, author string) ([]storage.Conflict, error) {
_, err := db.ExecContext(ctx, "CALL DOLT_MERGE('--author', ?, ?)", author, branch)
if err != nil {
// Check if the error is due to conflicts.
conflicts, conflictErr := GetConflicts(ctx, db)
if conflictErr == nil && len(conflicts) > 0 {
return conflicts, nil
}
if isMergeConflictAutocommitError(err) {
return nil, fmt.Errorf("merge branch %s: %w (resolve with: bd vc merge %s --strategy ours|theirs)", branch, err, branch)
}
return nil, fmt.Errorf("merge branch %s: %w", branch, err)
}
return nil, nil
}
// isMergeConflictAutocommitError reports whether err is Dolt's autocommit
// rejection of a conflicted merge (Error 1105, "@autocommit must be disabled
// so that merge conflicts can be resolved ..."). It is the shape Merge
// produces for every real conflict, since it runs under autocommit with
// neither dolt_allow_commit_conflicts nor a pinned session (#4992) — matched
// on message because the embedded engine and the MySQL driver report it as
// different error types.
func isMergeConflictAutocommitError(err error) bool {
if err == nil {
return false
}
msg := strings.ToLower(err.Error())View on GitHub (pinned to 71377f2769)
Solutions
- Re-run with a strategy: bd vc merge <branch> --strategy ours (keep local) or --strategy theirs (take incoming), or call MergeWithStrategy in code.
- Alternatively resolve out-of-band: inspect dolt_conflicts, run DOLT_CONFLICTS_RESOLVE('--ours'|'--theirs', table) per table, then commit.
- If conflicts should have been auto-resolved, ensure you are on a version with MergeAndSettle/TryAutoResolveMergeConflicts and use the Pull path that routes through it.
- Avoid bare Merge for branches known to conflict; default automation to the strategy variant.
Example fix
// before conflicts, err := versioncontrolops.Merge(ctx, db, "feature", "BD <bd@local>") // Error 1105 autocommit rejection // after conflicts, err := versioncontrolops.MergeWithStrategy(ctx, db, "feature", "", "ours") // conflicts resolved as ours
Defensive patterns
Strategy: fallback
Validate before calling
// prefer the strategy-aware merge when conflicts are possible // (bare Merge cannot resolve conflicts under autocommit) useStrategyMerge := true _ = useStrategyMerge
Type guard
func isAutocommitConflictErr(err error) bool {
if err == nil { return false }
m := strings.ToLower(err.Error())
return strings.Contains(m, "merge conflict") && strings.Contains(m, "autocommit")
} Try / catch
conflicts, err := versioncontrolops.Merge(ctx, db, branch, author)
if err != nil && isAutocommitConflictErr(err) {
// fall back to the strategy path the error message recommends
_, err = versioncontrolops.MergeWithStrategy(ctx, db, branch, author, "ours")
if err != nil { return fmt.Errorf("merge with strategy failed: %w", err) }
return nil
}
if err != nil { return err } Prevention
- Default to MergeWithStrategy (or bd vc merge --strategy ours|theirs) in any automated flow; reserve bare Merge for known-fast-forward merges.
- Fetch and pull before merging long-lived branches to shrink the conflict window.
- Parse the actionable suffix '(resolve with: bd vc merge ... --strategy ...)' in tooling instead of failing silently.
- Keep sessions conflict-tolerant (pinned session / dolt_allow_commit_conflicts) when you need to inspect dolt_conflicts after a failed merge.
When it happens
Trigger: Calling Merge (not MergeWithStrategy) on a branch whose merge produces row-level conflicts; GetConflicts returns nothing readable because Dolt rejected the implicit transaction; the session lacks the conflict-tolerant flags (dolt_allow_commit_conflicts / pinned session).
Common situations: Two machines edited the same issues offline then merged; scripting bd vc merge without --strategy when both sides touched identical rows; older automation written before the strategy flag existed.
Related errors
- merge %s: %w
- fast-forward to %s: %w
- merge branch %s: %w
- dolt auto-commit: %w
- failed to commit pending changes before pull: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/0b960bb690506d49.
Report an issue: GitHub.