{"record":{"id":"3ecc3161827286d9","repo":"gitbutlerapp/gitbutler","slug":"deduping-a-nonempty-will-never-make-it-empty","errorCode":null,"errorMessage":"deduping a NonEmpty will never make it empty","messagePattern":"deduping a NonEmpty will never make it empty","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/but/src/command/legacy/squash.rs","lineNumber":1703,"sourceCode":"        .commits\n        .iter()\n        .map(|commit| commit.id)\n        .map(|id| CommitId::try_from_commit_id(id, repo))\n        .collect::<anyhow::Result<Vec<_>>>()?;\n    Ok(commits_in_segment)\n}\n\nfn non_empty_dedup_maintain_sort<T>(non_empty: NonEmpty<T>) -> NonEmpty<T>\nwhere\n    T: Ord,\n{\n    let mut out = Vec::new();\n    for item in non_empty {\n        if !out.contains(&item) {\n            out.push(item);\n        }\n    }\n    NonEmpty::from_vec(out).expect(\"deduping a NonEmpty will never make it empty\")\n}\n","sourceCodeStart":1685,"sourceCodeEnd":1705,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/2497b8007aa4a1922dae9a805b32ffe5b5037785/crates/but/src/command/legacy/squash.rs#L1685-L1705","documentation":"Near-unreachable invariant in non_empty_dedup_maintain_sort: deduplicating a NonEmpty always retains its first element, so the output Vec is never empty and from_vec always succeeds. A panic here would require a modified loop that filters out every element including the first - ordinary code changes cannot produce it, and it cannot be triggered by input.","triggerScenarios":"Only a patched or future version of the loop that drops the first item (for example switching to 'retain' on the output while excluding the head); no runtime input reaches it.","commonSituations":"Practically none; relevant as a code-review note when touching this helper during squash-related refactors.","solutions":["No user action; if observed, suspect a locally modified build","Maintainer: construct the result as NonEmpty { head, tail } directly and delete the expect"],"exampleFix":"// before\nNonEmpty::from_vec(out).expect(\"deduping a NonEmpty will never make it empty\")\n\n// after - keep non-emptiness structural\nlet mut it = non_empty.into_iter();\nlet head = it.next().expect(\"iterator over NonEmpty yields one\");\nlet mut tail = Vec::new();\nlet mut seen = vec![head.clone()];\nfor item in it {\n    if !seen.contains(&item) {\n        seen.push(item.clone());\n        tail.push(item);\n    }\n}\nNonEmpty { head, tail }","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["When refactoring the dedup helper, construct NonEmpty { head, tail } directly to keep the invariant structural","Do not replace the element-by-element loop with a blanket retain/dedup that could drop the head","Treat any hit of this expect as evidence of a locally patched build, not a data problem"],"tags":["rust","panic","nonempty","invariant","unreachable","squash"],"backgroundTag":"nonempty-invariant-violation","analyzedSha":"2497b8007aa4a1922dae9a805b32ffe5b5037785","analyzedAt":"2026-08-17T00:30:25.648Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}