gitbutlerapp/gitbutler · error

source branches is already checked to be non-empty

Error message

source branches is already checked to be non-empty

What it means

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.

Source

Thrown at crates/but/src/command/legacy/squash.rs:919

                        true
                    }
                });
                commits_on_branch_sources.append(&mut commits_on_branch);

                if !target_commit_exists_on_branch {
                    branches_to_remove.push(source_branch_name.clone());
                }
            }
            BranchSquashTarget::Uncommitted => {
                branches_to_remove.push(source_branch_name.clone());
                commits_on_branch_sources.append(&mut commits_on_branch);
            }
        }
        source_branches.push(source_branch_name);
    }

    let source_branches = NonEmpty::from_vec(source_branches)
        .expect("source branches is already checked to be non-empty");

    Ok(ResolvedSquash::Branches {
        target,
        source_commits: commits_on_branch_sources,
        source_branches,
        branches_to_remove,
    })
}

fn resolve_reword(
    message: Option<Vec<String>>,
    no_message: bool,
    use_target_message: bool,
    use_source_message: bool,
) -> CliResult<HowToRewordTarget> {
    if use_target_message {
        Ok(HowToRewordTarget::UseTargetMessage)
    } else if use_source_message {

View on GitHub (pinned to 2497b8007a)

Solutions

  1. Verify the command was invoked with at least one source (branch/commit) - empty sources should be rejected at parse time
  2. Report the invocation; maintainers should keep the early non-empty check adjacent to parsing
  3. Convert the expect into an internal-error result for diagnosability

Example fix

// before
let source_branches = NonEmpty::from_vec(source_branches)
    .expect("source branches is already checked to be non-empty");

// after
let source_branches = NonEmpty::from_vec(source_branches)
    .ok_or_else(|| anyhow::anyhow!("internal: squash sources validated non-empty but resolved empty"))?;
Defensive patterns

Strategy: validation

Validate before calling

// Reject empty sources at parse time, adjacent to the same function
let source_branches = NonEmpty::from_vec(source_branches).ok_or_else(|| {
    bad_input("squash needs at least one source branch").arg_name("<SOURCES>")
})?;

Prevention

When it happens

Trigger: 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.

Common situations: Refactors of squash argument parsing and validation; empty --source lists that used to be rejected earlier in the flow.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17). Data as JSON: /api/errors/b395cf7d7ef53534. Report an issue: GitHub.