xai-org/grok-build · error · anyhow::Error

merge of '{conv_branch}' into '{target_branch}' failed: {}

Error message

merge of '{conv_branch}' into '{target_branch}' failed: {}

What it means

In the merge-to-main flow, when `git merge` of the session branch into the target branch fails without producing a structured Conflicts outcome, the code checks out the session branch for safety and bails with the scrubbed merge output. This covers hard merge failures rather than cleanly detected conflicts.

Source

Thrown at crates/codegen/xai-grok-workspace/src/session/git.rs:3314

        });
    }
    let in_progress = git_cli_raw(git_root, &["rev-parse", "-q", "--verify", "MERGE_HEAD"])
        .await?
        .0;
    if in_progress {
        let files = git_cli(git_root, &["diff", "--name-only", "--diff-filter=U"])
            .await?
            .lines()
            .map(str::to_owned)
            .collect();
        let _ = git_cli_raw(git_root, &["merge", "--abort"]).await;
        checkout_branch(git_root, conv_branch, false).await?;
        return Ok(GitMergeToMainResult {
            outcome: GitMergeToMainOutcome::Conflicts { files },
        });
    }
    let _ = checkout_branch(git_root, conv_branch, false).await;
    anyhow::bail!(
        "merge of '{conv_branch}' into '{target_branch}' failed: {}",
        scrub_git_output(&merge_out)
    )
}
/// Push the merged target to origin when `push` is set, failing loudly (never
/// forcing) so publish never records a deploy against an unpushed target.
async fn push_merged_target_if_requested(
    git_root: &Path,
    target_branch: &str,
    push: bool,
) -> Result<()> {
    if !push {
        return Ok(());
    }
    let (status, out) = push_classified(git_root).await?;
    anyhow::ensure!(
        status == PushStatus::Ok,
        "merge into '{target_branch}' succeeded but push failed ({status:?}): {}",

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Read the scrubbed merge_out in the message and fix the underlying git error
  2. Clean or commit untracked files that would be overwritten by the merge (`git clean -fd` / commit)
  3. Verify both branches exist locally: `git rev-parse --verify <branch>`
  4. Abort any partial merge (`git merge --abort`) and retry

Example fix

// before
merge_to_main(git_root, conv_branch, "main").await?;
// after
git status --porcelain         # clean worktree first
git merge --abort 2>/dev/null || true
merge_to_main(git_root, conv_branch, "main").await?;
Defensive patterns

Strategy: try-catch

Validate before calling

for b in [conv_branch, target_branch] {
    let ok = std::process::Command::new("git").arg("-C").arg(git_root).args(["rev-parse","--verify",b]).output()?;
    if !ok.status.success() { anyhow::bail!("branch {b} missing"); }
}
if !git_root.join(".git/index.lock").exists() { /* ok */ } else { anyhow::bail!("index.lock present"); }

Type guard

fn merge_failed_not_conflicts(msg: &str) -> bool { msg.contains("into '") && msg.contains("failed") }

Try / catch

match merge_to_main(git_root, conv_branch, target).await {
    Ok(r) => match r.outcome { Conflicts{files} => resolve(files), _ => ok() },
    Err(e) if e.to_string().contains("merge of '") => {
        let _ = git_cli(git_root, &["merge","--abort"]).await;
        Err(e.context("clean worktree / verify branches, then retry"))
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: Merging conv_branch into target_branch exits non-zero without the conflict-detection path triggering — e.g. merge aborted due to dirty state, refs not found, or git refusing the merge.

Common situations: Untracked files in target branch that the merge would overwrite; missing/renamed session branch; index.lock contention; unrelated histories.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/5c636b1e3721b4f3. Report an issue: GitHub.