Hmbown/CodeWhale · error · anyhow::Error

git worktree add failed for branch {} at {}{}{}

Error message

git worktree add failed for branch {} at {}{}{}

What it means

provision_worktree() ran `git worktree add -b <branch> <path> <base>` inside repo_root and git exited non-zero. The message embeds the branch, the target path, and git's trimmed stderr, so the real cause is in the appended detail. Typical causes are a branch that already exists, a non-empty target directory, or a bad base ref.

Source

Thrown at crates/lane/src/worktree.rs:60

    let base = spec.base_ref.as_deref().unwrap_or("HEAD");
    // Capture git output instead of inheriting the caller's terminal. Runtime
    // callers include the raw-mode TUI launch screen, where even one inherited
    // progress/error line corrupts the alternate-screen buffer.
    let output = Command::new("git")
        .current_dir(&spec.repo_root)
        .args([
            "worktree",
            "add",
            "-b",
            &spec.branch,
            &spec.path.to_string_lossy(),
            base,
        ])
        .output()
        .context("git worktree add")?;
    if !output.status.success() {
        let detail = String::from_utf8_lossy(&output.stderr).trim().to_string();
        bail!(
            "git worktree add failed for branch {} at {}{}{}",
            spec.branch,
            spec.path.display(),
            if detail.is_empty() { "" } else { ": " },
            detail
        );
    }
    Ok(ProvisionedWorktree {
        path: spec.path.clone(),
        branch: spec.branch.clone(),
    })
}

/// Remove a worktree when TTL has expired (or immediately when TTL is 0).
///
/// `stopped_at` is RFC3339. When `ttl_secs` is `None`, no cleanup is performed.
pub fn remove_worktree_if_expired(
    worktree_path: &Path,

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Read the stderr detail in the message first — 'already exists', 'already registered', and 'not a valid ref' each have different fixes
  2. If the branch exists: delete it (`git branch -D <branch>`) or reuse the existing worktree instead of provisioning a new one
  3. Run `git worktree prune` and remove the stale target directory, then retry with the same spec
  4. Validate base_ref first with `git rev-parse --verify <base_ref>^{commit}` before provisioning
  5. Generate collision-free branch names (e.g. include a lane id or timestamp) when re-provisioning is expected

Example fix

// before
let wt = provision_worktree(&spec)?;

// after: recover from the two common collisions
let wt = match provision_worktree(&spec) {
    Ok(wt) => wt,
    Err(err) if err.to_string().contains("already exists") => {
        Command::new("git")
            .args(["branch", "-D", &spec.branch])
            .current_dir(&spec.repo_root)
            .status()?;
        provision_worktree(&spec)?
    }
    Err(err) => return Err(err),
};
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight the two common causes:
let out = Command::new("git").current_dir(&spec.repo_root)
    .args(["rev-parse", "--verify", &format!("{}^{{commit}}", base)])
    .output()?;
if !out.status.success() { bail!("base ref {base} does not resolve"); }
if spec.path.exists() && std::fs::read_dir(&spec.path)?.next().is_some() {
    bail!("worktree path {} not empty", spec.path.display());
}

Try / catch

match provision_worktree(&spec) {
    Ok(wt) => Ok(wt),
    Err(err) => {
        let msg = format!("{err:#}");
        if msg.contains("already exists") || msg.contains("already registered") {
            // branch or path collision: clean up and retry once
            cleanup_branch_and_path(&spec)?;
            provision_worktree(&spec)
        } else {
            Err(err)
        }
    }
}

Prevention

When it happens

Trigger: Re-provisioning a lane whose branch already exists locally; specifying a worktree path that already contains files (including a stale worktree from a crashed prior run); passing base_ref that names a ref that does not exist (e.g. origin/feature after a remote rename); running while HEAD is unborn in a fresh `git init` clone; repo_root pointing at a directory without a valid .git.

Common situations: Retry-after-crash flows that recreate the same lane/branch name; parallel lanes colliding on generated branch names; shallow or bare checkouts where the expected base ref is absent; leftover worktree metadata after a force-deleted directory (needs `git worktree prune`).

Understand the failure class

Background: "git command failed": what it means when a tool shells out to git and git exits non-zero — this error's family across 21 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/4933b34de0e873d7. Report an issue: GitHub.