{"record":{"id":"4933b34de0e873d7","repo":"Hmbown/CodeWhale","slug":"git-worktree-add-failed-for-branch-at","errorCode":null,"errorMessage":"git worktree add failed for branch {} at {}{}{}","messagePattern":"git worktree add failed for branch (.+?) at (.+?)(.+?)(.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/lane/src/worktree.rs","lineNumber":60,"sourceCode":"    let base = spec.base_ref.as_deref().unwrap_or(\"HEAD\");\n    // Capture git output instead of inheriting the caller's terminal. Runtime\n    // callers include the raw-mode TUI launch screen, where even one inherited\n    // progress/error line corrupts the alternate-screen buffer.\n    let output = Command::new(\"git\")\n        .current_dir(&spec.repo_root)\n        .args([\n            \"worktree\",\n            \"add\",\n            \"-b\",\n            &spec.branch,\n            &spec.path.to_string_lossy(),\n            base,\n        ])\n        .output()\n        .context(\"git worktree add\")?;\n    if !output.status.success() {\n        let detail = String::from_utf8_lossy(&output.stderr).trim().to_string();\n        bail!(\n            \"git worktree add failed for branch {} at {}{}{}\",\n            spec.branch,\n            spec.path.display(),\n            if detail.is_empty() { \"\" } else { \": \" },\n            detail\n        );\n    }\n    Ok(ProvisionedWorktree {\n        path: spec.path.clone(),\n        branch: spec.branch.clone(),\n    })\n}\n\n/// Remove a worktree when TTL has expired (or immediately when TTL is 0).\n///\n/// `stopped_at` is RFC3339. When `ttl_secs` is `None`, no cleanup is performed.\npub fn remove_worktree_if_expired(\n    worktree_path: &Path,","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/lane/src/worktree.rs#L42-L78","documentation":"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.","triggerScenarios":"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.","commonSituations":"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`).","solutions":["Read the stderr detail in the message first — 'already exists', 'already registered', and 'not a valid ref' each have different fixes","If the branch exists: delete it (`git branch -D <branch>`) or reuse the existing worktree instead of provisioning a new one","Run `git worktree prune` and remove the stale target directory, then retry with the same spec","Validate base_ref first with `git rev-parse --verify <base_ref>^{commit}` before provisioning","Generate collision-free branch names (e.g. include a lane id or timestamp) when re-provisioning is expected"],"exampleFix":"// before\nlet wt = provision_worktree(&spec)?;\n\n// after: recover from the two common collisions\nlet wt = match provision_worktree(&spec) {\n    Ok(wt) => wt,\n    Err(err) if err.to_string().contains(\"already exists\") => {\n        Command::new(\"git\")\n            .args([\"branch\", \"-D\", &spec.branch])\n            .current_dir(&spec.repo_root)\n            .status()?;\n        provision_worktree(&spec)?\n    }\n    Err(err) => return Err(err),\n};","handlingStrategy":"try-catch","validationCode":"// Pre-flight the two common causes:\nlet out = Command::new(\"git\").current_dir(&spec.repo_root)\n    .args([\"rev-parse\", \"--verify\", &format!(\"{}^{{commit}}\", base)])\n    .output()?;\nif !out.status.success() { bail!(\"base ref {base} does not resolve\"); }\nif spec.path.exists() && std::fs::read_dir(&spec.path)?.next().is_some() {\n    bail!(\"worktree path {} not empty\", spec.path.display());\n}","typeGuard":null,"tryCatchPattern":"match provision_worktree(&spec) {\n    Ok(wt) => Ok(wt),\n    Err(err) => {\n        let msg = format!(\"{err:#}\");\n        if msg.contains(\"already exists\") || msg.contains(\"already registered\") {\n            // branch or path collision: clean up and retry once\n            cleanup_branch_and_path(&spec)?;\n            provision_worktree(&spec)\n        } else {\n            Err(err)\n        }\n    }\n}","preventionTips":["Include a lane id in generated branch names so re-provisioning cannot collide","Run `git worktree prune` before provisioning in recovery flows","Always resolve base_ref to a commit hash before passing it"],"tags":["git","worktree","branch","subprocess","lane"],"backgroundTag":"git-command-failed","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}