{"record":{"id":"b395cf7d7ef53534","repo":"gitbutlerapp/gitbutler","slug":"source-branches-is-already-checked-to-be-non-empty","errorCode":null,"errorMessage":"source branches is already checked to be non-empty","messagePattern":"source branches is already checked to be non-empty","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but/src/command/legacy/squash.rs","lineNumber":919,"sourceCode":"                        true\n                    }\n                });\n                commits_on_branch_sources.append(&mut commits_on_branch);\n\n                if !target_commit_exists_on_branch {\n                    branches_to_remove.push(source_branch_name.clone());\n                }\n            }\n            BranchSquashTarget::Uncommitted => {\n                branches_to_remove.push(source_branch_name.clone());\n                commits_on_branch_sources.append(&mut commits_on_branch);\n            }\n        }\n        source_branches.push(source_branch_name);\n    }\n\n    let source_branches = NonEmpty::from_vec(source_branches)\n        .expect(\"source branches is already checked to be non-empty\");\n\n    Ok(ResolvedSquash::Branches {\n        target,\n        source_commits: commits_on_branch_sources,\n        source_branches,\n        branches_to_remove,\n    })\n}\n\nfn resolve_reword(\n    message: Option<Vec<String>>,\n    no_message: bool,\n    use_target_message: bool,\n    use_source_message: bool,\n) -> CliResult<HowToRewordTarget> {\n    if use_target_message {\n        Ok(HowToRewordTarget::UseTargetMessage)\n    } else if use_source_message {","sourceCodeStart":901,"sourceCodeEnd":937,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/2497b8007aa4a1922dae9a805b32ffe5b5037785/crates/but/src/command/legacy/squash.rs#L901-L937","documentation":"Invariant at the end of squash source resolution: the loop pushes one source_branches entry per iteration, and the loop only runs over inputs already validated to contain at least one source branch, so the final NonEmpty::from_vec cannot fail. A panic means earlier non-empty validation was bypassed - for example a parser or refactor change let an empty sources list reach this code.","triggerScenarios":"A refactor removes or weakens the earlier 'at least one source' validation; a code path returns from the loop without pushing yet still reaches the re-wrap; a new squash mode processes zero sources.","commonSituations":"Refactors of squash argument parsing and validation; empty --source lists that used to be rejected earlier in the flow.","solutions":["Verify the command was invoked with at least one source (branch/commit) - empty sources should be rejected at parse time","Report the invocation; maintainers should keep the early non-empty check adjacent to parsing","Convert the expect into an internal-error result for diagnosability"],"exampleFix":"// before\nlet source_branches = NonEmpty::from_vec(source_branches)\n    .expect(\"source branches is already checked to be non-empty\");\n\n// after\nlet source_branches = NonEmpty::from_vec(source_branches)\n    .ok_or_else(|| anyhow::anyhow!(\"internal: squash sources validated non-empty but resolved empty\"))?;","handlingStrategy":"validation","validationCode":"// Reject empty sources at parse time, adjacent to the same function\nlet source_branches = NonEmpty::from_vec(source_branches).ok_or_else(|| {\n    bad_input(\"squash needs at least one source branch\").arg_name(\"<SOURCES>\")\n})?;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate non-empty sources once at the CLI boundary and keep the check next to the re-wrap","When refactoring squash parsing, re-run the squash CLI tests covering single and multiple sources","Prefer ok_or_else with a user-facing message over expect for this pattern"],"tags":["rust","panic","nonempty","invariant","squash"],"backgroundTag":"nonempty-invariant-violation","analyzedSha":"2497b8007aa4a1922dae9a805b32ffe5b5037785","analyzedAt":"2026-08-17T00:30:25.648Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}