{"record":{"id":"3ccb7686a55b59ba","repo":"affaan-m/ECC","slug":"git-add-failed-for-path-stderr","errorCode":null,"errorMessage":"git add failed for {path}: {stderr}","messagePattern":"git add failed for (.+?): (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"ecc2/src/worktree/mod.rs","lineNumber":304,"sourceCode":"    Ok(String::from_utf8_lossy(&output.stdout)\n        .lines()\n        .filter_map(parse_git_status_entry)\n        .collect())\n}\n\npub fn stage_path(worktree: &WorktreeInfo, path: &str) -> Result<()> {\n    let output = Command::new(\"git\")\n        .arg(\"-C\")\n        .arg(&worktree.path)\n        .args([\"add\", \"--\"])\n        .arg(path)\n        .output()\n        .with_context(|| format!(\"Failed to stage {}\", path))?;\n    if output.status.success() {\n        Ok(())\n    } else {\n        let stderr = String::from_utf8_lossy(&output.stderr);\n        anyhow::bail!(\"git add failed for {path}: {stderr}\");\n    }\n}\n\npub fn unstage_path(worktree: &WorktreeInfo, path: &str) -> Result<()> {\n    let output = Command::new(\"git\")\n        .arg(\"-C\")\n        .arg(&worktree.path)\n        .args([\"reset\", \"HEAD\", \"--\"])\n        .arg(path)\n        .output()\n        .with_context(|| format!(\"Failed to unstage {}\", path))?;\n    if output.status.success() {\n        Ok(())\n    } else {\n        let stderr = String::from_utf8_lossy(&output.stderr);\n        anyhow::bail!(\"git reset failed for {path}: {stderr}\");\n    }\n}","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/ecc2/src/worktree/mod.rs#L286-L322","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nstage_path(&worktree, path)?;\n\n// after\nlet target = worktree.path.join(path);\nif !target.exists() {\n    anyhow::bail!(\"cannot stage, path no longer exists: {}\", target.display());\n}\nstage_path(&worktree, path)?;","handlingStrategy":"validation","validationCode":"fn stageable(worktree: &WorktreeInfo, path: &str) -> bool {\n    worktree.path.join(path).exists()\n}\n\nif !stageable(&worktree, path) {\n    return Err(anyhow!(\"path does not exist; refresh status\"));\n}\nstage_path(&worktree, path)?;","typeGuard":null,"tryCatchPattern":"match stage_path(&worktree, path) {\n    Ok(()) => { /* refresh status */ }\n    Err(e) => {\n        let msg = format!(\"{e:#}\");\n        if msg.contains(\"index.lock\") {\n            // surface 'another git operation in progress'\n        } else if msg.contains(\"ignored\") {\n            // ask user whether to force-add\n        } else {\n            return Err(e);\n        }\n    }\n}","preventionTips":["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."],"tags":["git","subprocess","worktree","index","staging"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}