{"record":{"id":"31f57a6c7d81a9a7","repo":"gitbutlerapp/gitbutler","slug":"key-was-just-inserted","errorCode":null,"errorMessage":"key was just inserted","messagePattern":"key was just inserted","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"info","filePath":"crates/but-workspace/src/commit/uncommit_changes.rs","lineNumber":296,"sourceCode":"            }\n            Entry::Occupied(mut entry) => {\n                let existing = entry.get_mut();\n                if existing.hunk_headers.is_empty() || spec.hunk_headers.is_empty() {\n                    // Whole-file removal supersedes any hunk selection.\n                    existing.hunk_headers.clear();\n                } else {\n                    for header in spec.hunk_headers {\n                        if !existing.hunk_headers.contains(&header) {\n                            existing.hunk_headers.push(header);\n                        }\n                    }\n                }\n            }\n        }\n    }\n    order\n        .into_iter()\n        .map(|key| by_path.remove(&key).expect(\"key was just inserted\"))\n        .collect()\n}\n\nfn failure(group: &GroupedUncommitChanges, error: impl Into<String>) -> UncommitChangesFailure {\n    UncommitChangesFailure {\n        commit_id: group.commit_id,\n        changes: group.changes.clone(),\n        error: error.into(),\n    }\n}\n","sourceCodeStart":278,"sourceCodeEnd":307,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-workspace/src/commit/uncommit_changes.rs#L278-L307","documentation":"Panic from `expect(\"key was just inserted\")` in `merge_specs_by_path` (but-workspace uncommit_changes.rs:296). The function maintains two structures in lockstep: `order` records each first-seen `(previous_path, path)` key and `by_path` inserts that same key. The final pass removes each recorded key from the map, so every removal must hit; the expect asserts the map-side of that pairing. Unreachable unless the two structures are desynchronized by an edit.","triggerScenarios":"Uncommitting selected changes where multiple DiffSpecs target the same file (per-hunk selections), which routes through this merge function; the panic requires a code change that pushes to `order` without inserting into `by_path` (or with a different key), not any input to the shipped code.","commonSituations":"Maintainers adding rename-aware merging or path normalization between the push and the insert; forks altering the key type; none for end users.","solutions":["No caller-side action — keys are inserted and recorded in the same match arm","If editing, consider a single structure (e.g. `IndexMap`) so order and storage cannot diverge","Add a unit test with duplicate paths mixing whole-file and per-hunk specs"],"exampleFix":"// before (two structures kept in sync manually)\nlet mut order = Vec::new();\nlet mut by_path = HashMap::new();\n...\norder.into_iter().map(|key| by_path.remove(&key).expect(\"key was just inserted\")).collect()\n\n// after: one structure, no sync to break\nuse indexmap::IndexMap;\nlet mut by_path = IndexMap::new();\n// merge into by_path.entry(key) ...\nby_path.into_iter().map(|(_, spec)| spec).collect::<Vec<_>>()","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["No input can trigger this in shipped code; order and map are updated in the same match arm","Maintainers: consider IndexMap to eliminate the two-structure sync, and test duplicate-path spec merging"],"tags":["rust","panic","expect","hashmap-sync","diffspec-merge","unreachable","uncommit"],"backgroundTag":"internal-invariant-panic","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}