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
- Read the scrubbed merge_out in the message and fix the underlying git error
- Clean or commit untracked files that would be overwritten by the merge (`git clean -fd` / commit)
- Verify both branches exist locally: `git rev-parse --verify <branch>`
- 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
- Keep the worktree clean before initiating merges
- Ensure both branches exist locally (fetch first)
- Never run concurrent git operations in the same worktree
- Clear stale index.lock after abnormal terminations
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
- merge of base ref '{base}' failed: {merge_out}
- git status failed: {}
- git worktree add failed: {}
- git fetch --no-tags origin {spec} failed ({status}): {stderr
- local '{target_branch}' has diverged from origin; resolve be
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/5c636b1e3721b4f3.
Report an issue: GitHub.