{"record":{"id":"6d76c81d26dc0ffd","repo":"affaan-m/ECC","slug":"selected-hunk-is-not-staged","errorCode":null,"errorMessage":"selected hunk is not staged","messagePattern":"selected hunk is not staged","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"ecc2/src/worktree/mod.rs","lineNumber":416,"sourceCode":"        }))\n    }\n}\n\npub fn stage_hunk(worktree: &WorktreeInfo, hunk: &GitPatchHunk) -> Result<()> {\n    if hunk.section != GitPatchSectionKind::Unstaged {\n        anyhow::bail!(\"selected hunk is already staged\");\n    }\n    git_apply_patch(\n        &worktree.path,\n        &[\"--cached\"],\n        &hunk.patch,\n        \"stage selected hunk\",\n    )\n}\n\npub fn unstage_hunk(worktree: &WorktreeInfo, hunk: &GitPatchHunk) -> Result<()> {\n    if hunk.section != GitPatchSectionKind::Staged {\n        anyhow::bail!(\"selected hunk is not staged\");\n    }\n    git_apply_patch(\n        &worktree.path,\n        &[\"-R\", \"--cached\"],\n        &hunk.patch,\n        \"unstage selected hunk\",\n    )\n}\n\npub fn reset_hunk(\n    worktree: &WorktreeInfo,\n    entry: &GitStatusEntry,\n    hunk: &GitPatchHunk,\n) -> Result<()> {\n    if entry.untracked {\n        anyhow::bail!(\"cannot reset hunks for untracked files\");\n    }\n","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/ecc2/src/worktree/mod.rs#L398-L434","documentation":"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.","triggerScenarios":"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`.","commonSituations":"Keyboard shortcut for 'unstage' active while cursor sits on an unstaged hunk; hunk reference held across a status refresh that moved it into Unstaged.","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."],"exampleFix":"// before\nunstage_hunk(&worktree, &hunk)?;\n\n// after\nmatch hunk.section {\n    GitPatchSectionKind::Staged => unstage_hunk(&worktree, &hunk)?,\n    GitPatchSectionKind::Unstaged => { /* not staged; no-op */ }\n}","handlingStrategy":"validation","validationCode":"use crate::worktree::{GitPatchHunk, GitPatchSectionKind, unstage_hunk};\n\nfn try_unstage_hunk(worktree: &WorktreeInfo, hunk: &GitPatchHunk) -> anyhow::Result<()> {\n    if hunk.section != GitPatchSectionKind::Staged {\n        return Ok(()); // not staged — nothing to unstage\n    }\n    unstage_hunk(worktree, hunk)\n}","typeGuard":"fn is_staged(hunk: &GitPatchHunk) -> bool {\n    matches!(hunk.section, GitPatchSectionKind::Staged)\n}","tryCatchPattern":"match unstage_hunk(&worktree, &hunk) {\n    Ok(()) => { /* refresh */ }\n    Err(e) if format!(\"{e}\").contains(\"not staged\") => { /* benign no-op */ }\n    Err(e) => return Err(e),\n}","preventionTips":["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."],"tags":["git","worktree","state-machine","validation","hunk"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}