affaan-m/ECC · warning · anyhow::Error

Merge blocked between {left_branch} and {right_branch} by {}

Error message

Merge blocked between {left_branch} and {right_branch} by {} conflict(s): {detail}

What it means

merge_into_base calls merge_readiness, and when the status is Conflicted it bails with `readiness.summary`. The summary string is the conflict report assembled by merge_readiness_for_branches (count of conflicts plus up to three path examples). This is a guarded refusal to perform a merge that git has already predicted will conflict.

Source

Thrown at ecc2/src/worktree/mod.rs:861

    } else {
        Ok(WorktreeHealth::InProgress)
    }
}

pub fn has_uncommitted_changes(worktree: &WorktreeInfo) -> Result<bool> {
    Ok(!git_status_short(&worktree.path)?.is_empty())
}

pub fn has_staged_changes(worktree: &WorktreeInfo) -> Result<bool> {
    Ok(git_status_entries(worktree)?
        .iter()
        .any(|entry| entry.staged))
}

pub fn merge_into_base(worktree: &WorktreeInfo) -> Result<MergeOutcome> {
    let readiness = merge_readiness(worktree)?;
    if readiness.status == MergeReadinessStatus::Conflicted {
        anyhow::bail!(readiness.summary);
    }

    if has_uncommitted_changes(worktree)? {
        anyhow::bail!(
            "Worktree {} has uncommitted changes; commit or discard them before merging",
            worktree.branch
        );
    }

    let repo_root = base_checkout_path(worktree)?;
    let current_branch = get_current_branch(&repo_root)?;
    if current_branch != worktree.base_branch {
        anyhow::bail!(
            "Base branch {} is not checked out in repo root (currently {})",
            worktree.base_branch,
            current_branch
        );
    }

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Rebase or manually resolve the conflicts before merging: `git -C <worktree> rebase <base>`.
  2. Use branch_conflict_preview to surface the conflicting paths to the user before they retry.
  3. After resolving, re-run merge_readiness to confirm status == Ready before calling merge_into_base.
  4. If the conflicts are spurious from stale refs, `git fetch` and re-derive readiness.

Example fix

// before
let outcome = merge_into_base(&worktree)?;

// after
let readiness = merge_readiness(&worktree)?;
if readiness.status == MergeReadinessStatus::Conflicted {
    return Err(anyhow!("merge has {} conflict(s): {}",
        readiness.conflicts.len(), readiness.conflicts.join(", ")));
}
let outcome = merge_into_base(&worktree)?;
Defensive patterns

Strategy: validation

Validate before calling

use crate::worktree::{merge_readiness, MergeReadinessStatus};

let readiness = merge_readiness(&worktree)?;
if readiness.status == MergeReadinessStatus::Conflicted {
    return Err(anyhow!(
        "merge blocked by {} conflict(s): {}",
        readiness.conflicts.len(),
        readiness.conflicts.join(", ")
    ));
}
let outcome = merge_into_base(&worktree)?;

Type guard

fn is_merge_ready(r: &MergeReadiness) -> bool {
    r.status == MergeReadinessStatus::Ready
}

Try / catch

match merge_into_base(&worktree) {
    Ok(outcome) => Ok(outcome),
    Err(e) => {
        let m = format!("{e:#}");
        if m.starts_with("Merge blocked") {
            // surface the conflict list to the user; suggest rebase or manual resolve
        }
        Err(e)
    }
}

Prevention

When it happens

Trigger: Calling merge_into_base on a worktree branch whose merge-tree preview against base_branch produced conflict paths (both sides modified the same regions). The bail happens before any working-tree mutation, so the repo is left untouched.

Common situations: Long-lived feature branch behind a rebased main; two agents working in different worktrees edited the same files and one tries to merge first; auto-merge pipeline hitting a genuinely conflicting change set.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/91252282302b8b44. Report an issue: GitHub.