affaan-m/ECC · error · anyhow::Error
git worktree list --porcelain failed
Error message
git worktree list --porcelain failed: {stderr} What it means
Raised when `git worktree list --porcelain` exits non-zero while the library is resolving the checkout path for a worktree's base branch. Since this command is the canonical source of worktree metadata, failure means git could not enumerate worktrees — corrupt .git/worktrees metadata, not a repo, or a git version too old to support --porcelain for worktree list.
Solutions
- Confirm the process cwd (repo root) is inside a git repository: 'git rev-parse --is-inside-work-tree'
- Run 'git worktree prune' to clean stale worktree admin metadata
- Check 'git --version' — upgrade git to >= 2.7 where --porcelain is supported for worktree list
- Read the stderr in the message for git's specific complaint (e.g. 'not a git repository')
- If a worktree dir was deleted manually, 'git worktree repair' or recreate it
Example fix
// before: assumes valid repo
let path = base_checkout_path(&wt)?;
// after: pre-check repo validity
let ok = Command::new("git")
.args(["rev-parse", "--is-inside-work-tree"])
.current_dir(&wt.path)
.output()?;
anyhow::ensure!(ok.status.success(), "{} is not a git repository", wt.path.display());
let path = base_checkout_path(&wt)?; Defensive patterns
Strategy: try-catch
Validate before calling
let is_repo = Command::new("git")
.args(["rev-parse", "--is-inside-work-tree"])
.current_dir(&wt.path)
.output()
.map(|o| o.status.success())
.unwrap_or(false);
anyhow::ensure!(is_repo, "{} is not a git repository", wt.path.display()); Try / catch
match base_checkout_path(&wt) {
Ok(p) => p,
Err(e) if e.to_string().contains("not a git repository") => {
eprintln!("worktree {} is outside a repo or has stale metadata", wt.path.display());
// 'git worktree prune' or 'git worktree repair' before retry
Err(e)
}
Err(e) => Err(e),
} Prevention
- Pin a modern git (>= 2.7) in CI images
- Run 'git worktree prune' after manually deleting worktree directories
- Verify cwd is inside the repo before invoking worktree commands
- Use 'git worktree repair' when moving repo directories
When it happens
Trigger: Calling worktree resolution outside a git repository (or with a corrupted GIT_DIR), with git < 2.7 lacking 'worktree list --porcelain', or with a damaged .git/worktrees directory containing stale admin files.
Common situations: Running the tool in a subdirectory that is not a repo (wrong cwd); pruned worktree metadata left behind after manual directory deletion; ancient system git on a CI container.
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
- Base branch is not checked out in repo root (currently )
- git diff failed
- git log failed
- git reset failed for
- git restore failed for
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/57c81ff2ec5123b1.
Report an issue: GitHub.
Appendix: source
Thrown at ecc2/src/worktree/mod.rs:1581
.arg(repo_root)
.args(["rev-parse", "--abbrev-ref", "HEAD"])
.output()
.context("Failed to get current branch")?;
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
}
fn base_checkout_path(worktree: &WorktreeInfo) -> Result<PathBuf> {
let output = Command::new("git")
.arg("-C")
.arg(&worktree.path)
.args(["worktree", "list", "--porcelain"])
.output()
.context("Failed to resolve git worktree list")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
anyhow::bail!("git worktree list --porcelain failed: {stderr}");
}
let target_branch = format!("refs/heads/{}", worktree.base_branch);
let mut current_path: Option<PathBuf> = None;
let mut current_branch: Option<String> = None;
let mut fallback: Option<PathBuf> = None;
for line in String::from_utf8_lossy(&output.stdout).lines() {
if line.is_empty() {
if let Some(path) = current_path.take() {
if fallback.is_none() && path != worktree.path {
fallback = Some(path.clone());
}
if current_branch.as_deref() == Some(target_branch.as_str())
&& path != worktree.path
{
return Ok(path);
}View on GitHub (pinned to 8321021c54)