affaan-m/ECC · warning · anyhow::Error
selected hunk is not staged
Error message
selected hunk is not staged
What it means
unstage_hunk rejects any hunk whose `section` is not `GitPatchSectionKind::Staged`. Symmetric to the stage_hunk guard: you cannot unstage a hunk that is not currently staged. The function then applies the patch in reverse against `--cached`, which only makes sense for staged content.
Source
Thrown at ecc2/src/worktree/mod.rs:416
}))
}
}
pub fn stage_hunk(worktree: &WorktreeInfo, hunk: &GitPatchHunk) -> Result<()> {
if hunk.section != GitPatchSectionKind::Unstaged {
anyhow::bail!("selected hunk is already staged");
}
git_apply_patch(
&worktree.path,
&["--cached"],
&hunk.patch,
"stage selected hunk",
)
}
pub fn unstage_hunk(worktree: &WorktreeInfo, hunk: &GitPatchHunk) -> Result<()> {
if hunk.section != GitPatchSectionKind::Staged {
anyhow::bail!("selected hunk is not staged");
}
git_apply_patch(
&worktree.path,
&["-R", "--cached"],
&hunk.patch,
"unstage selected hunk",
)
}
pub fn reset_hunk(
worktree: &WorktreeInfo,
entry: &GitStatusEntry,
hunk: &GitPatchHunk,
) -> Result<()> {
if entry.untracked {
anyhow::bail!("cannot reset hunks for untracked files");
}
View on GitHub (pinned to 01e15490f0)
Solutions
- Gate the call: only invoke unstage_hunk when `hunk.section == GitPatchSectionKind::Staged`.
- Refresh the patch view before dispatching the action.
- Disable the 'unstage' affordance when the selected hunk is in the Unstaged section.
Example fix
// before
unstage_hunk(&worktree, &hunk)?;
// after
match hunk.section {
GitPatchSectionKind::Staged => unstage_hunk(&worktree, &hunk)?,
GitPatchSectionKind::Unstaged => { /* not staged; no-op */ }
} Defensive patterns
Strategy: validation
Validate before calling
use crate::worktree::{GitPatchHunk, GitPatchSectionKind, unstage_hunk};
fn try_unstage_hunk(worktree: &WorktreeInfo, hunk: &GitPatchHunk) -> anyhow::Result<()> {
if hunk.section != GitPatchSectionKind::Staged {
return Ok(()); // not staged — nothing to unstage
}
unstage_hunk(worktree, hunk)
} Type guard
fn is_staged(hunk: &GitPatchHunk) -> bool {
matches!(hunk.section, GitPatchSectionKind::Staged)
} Try / catch
match unstage_hunk(&worktree, &hunk) {
Ok(()) => { /* refresh */ }
Err(e) if format!("{e}").contains("not staged") => { /* benign no-op */ }
Err(e) => return Err(e),
} Prevention
- Branch on `hunk.section` before dispatching unstage.
- Keep the unstage verb disabled on Unstaged hunks.
- Re-derive hunks from a fresh patch view before acting.
When it happens
Trigger: Passing a `GitPatchHunk` from the `--- Working tree diff ---` (Unstaged) section into unstage_hunk. UI reuses the same handler for both sections without checking `hunk.section`.
Common situations: Keyboard shortcut for 'unstage' active while cursor sits on an unstaged hunk; hunk reference held across a status refresh that moved it into Unstaged.
Related errors
- selected hunk is already staged
- cannot reset hunks for untracked files
- cannot reset a staged hunk while the file also has unstaged
- commit message cannot be empty
- no staged changes to commit
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/6d76c81d26dc0ffd.
Report an issue: GitHub.