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

  1. Re-run with a strategy: bd vc merge <branch> --strategy ours (keep local) or --strategy theirs (take incoming), or call MergeWithStrategy in code.
  2. Alternatively resolve out-of-band: inspect dolt_conflicts, run DOLT_CONFLICTS_RESOLVE('--ours'|'--theirs', table) per table, then commit.
  3. 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.
  4. 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

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


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