affaan-m/ECC · error · anyhow::Error
git add failed for {path}: {stderr}
Error message
git add failed for {path}: {stderr} What it means
stage_path runs `git -C <worktree.path> add -- <path>` and re-throws git's stderr on a non-zero exit. The `--` separator means the path is treated literally (no option injection), so failures are genuine git refusals, not parsing issues. The `with_context` wrapper covers the spawn-failure case (git binary missing); the bail! covers the ran-but-failed case.
Source
Thrown at ecc2/src/worktree/mod.rs:304
Ok(String::from_utf8_lossy(&output.stdout)
.lines()
.filter_map(parse_git_status_entry)
.collect())
}
pub fn stage_path(worktree: &WorktreeInfo, path: &str) -> Result<()> {
let output = Command::new("git")
.arg("-C")
.arg(&worktree.path)
.args(["add", "--"])
.arg(path)
.output()
.with_context(|| format!("Failed to stage {}", path))?;
if output.status.success() {
Ok(())
} else {
let stderr = String::from_utf8_lossy(&output.stderr);
anyhow::bail!("git add failed for {path}: {stderr}");
}
}
pub fn unstage_path(worktree: &WorktreeInfo, path: &str) -> Result<()> {
let output = Command::new("git")
.arg("-C")
.arg(&worktree.path)
.args(["reset", "HEAD", "--"])
.arg(path)
.output()
.with_context(|| format!("Failed to unstage {}", path))?;
if output.status.success() {
Ok(())
} else {
let stderr = String::from_utf8_lossy(&output.stderr);
anyhow::bail!("git reset failed for {path}: {stderr}");
}
}View on GitHub (pinned to 01e15490f0)
Solutions
- Re-fetch the status entry list right before staging and confirm the path still appears.
- If the file is gitignored and you genuinely want it staged, switch to `git add --force -- <path>` (requires a code change to stage_path).
- Remove `.git/index.lock` if a previous git process was killed.
- Ensure the path is relative to `worktree.path` (not to the process cwd) when invoking stage_path.
Example fix
// before
stage_path(&worktree, path)?;
// after
let target = worktree.path.join(path);
if !target.exists() {
anyhow::bail!("cannot stage, path no longer exists: {}", target.display());
}
stage_path(&worktree, path)?; Defensive patterns
Strategy: validation
Validate before calling
fn stageable(worktree: &WorktreeInfo, path: &str) -> bool {
worktree.path.join(path).exists()
}
if !stageable(&worktree, path) {
return Err(anyhow!("path does not exist; refresh status"));
}
stage_path(&worktree, path)?; Try / catch
match stage_path(&worktree, path) {
Ok(()) => { /* refresh status */ }
Err(e) => {
let msg = format!("{e:#}");
if msg.contains("index.lock") {
// surface 'another git operation in progress'
} else if msg.contains("ignored") {
// ask user whether to force-add
} else {
return Err(e);
}
}
} Prevention
- Refresh `git_status_entries` immediately before staging and pass paths straight from those entries.
- Keep paths relative to `worktree.path`, never to process cwd.
- Avoid concurrent index-mutating operations in the same worktree.
- If gitignore noise is expected, decide on a force-add policy explicitly rather than ad-hoc.
When it happens
Trigger: Staging a path that does not exist in the worktree, is matched by `.gitignore`/`.git/info/exclude` (without `-f`), is outside the repository boundary, or when a concurrent git process holds `.git/index.lock`. Also when the path is a gitlink/submodule pointer git refuses to add directly.
Common situations: UI passes a relative path computed against the wrong cwd; path was deleted between status refresh and stage action; `.gitignore` rule added after the file was tracked-then-removed; ESLint/prettier hook rewrote and removed the file mid-staging; submodule path passed where git expects a regular entry.
Related errors
- git reset failed for {path}: {stderr}
- git restore failed for {}: {stderr}
- git worktree add failed: {stderr}
- git status failed: {stderr}
- cannot reset a staged hunk while the file also has unstaged
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/3ccb7686a55b59ba.
Report an issue: GitHub.