gitbutlerapp/gitbutler · info

validated pick arity above

Error message

validated pick arity above

What it means

Panic from `expect("validated pick arity above")` in the interactive-integration parser (but-workspace integrate_branch_upstream/parsing.rs:53). The match arm first bails unless `arguments.len() == 1`, then takes `arguments.first()`; with exactly one element the `first()` cannot be None. It is an unreachable-by-construction assertion tying the unwrap to the arity check directly above it.

Source

Thrown at crates/but-workspace/src/branch/integrate_branch_upstream/parsing.rs:53

            .with_context(|| format!("line {line_number}: invalid message clause"))?;
        let tokens = command_part.split_whitespace().collect::<Vec<_>>();
        let Some(command) = tokens.first().copied() else {
            continue;
        };
        let arguments = tokens.get(1..).unwrap_or_default();

        let step = match command {
            "pick" => {
                if message_part.is_some() {
                    bail!("line {line_number}: pick does not accept a message clause");
                }
                if arguments.len() != 1 {
                    bail!("line {line_number}: pick requires exactly one commit");
                }
                let commit = arguments
                    .first()
                    .copied()
                    .expect("validated pick arity above");
                InteractiveIntegrationStep::Pick {
                    commit_id: resolve_commit(commit, &allowed_commits).map_err(|err| {
                        anyhow::anyhow!("line {line_number}: invalid pick commit: {err}")
                    })?,
                }
            }
            "merge" => {
                if message_part.is_some() {
                    bail!("line {line_number}: merge does not accept a message clause");
                }
                if arguments.len() != 1 {
                    bail!("line {line_number}: merge requires exactly one commit");
                }
                let commit = arguments
                    .first()
                    .copied()
                    .expect("validated merge arity above");
                InteractiveIntegrationStep::Merge {

View on GitHub (pinned to caf1f223d3)

Solutions

  1. No caller-side action; valid inputs are enforced by the bails above
  2. If editing the parser, derive the value from the length check itself (e.g. `let [commit] = arguments.as_slice() else { bail!(...) };`) so validation and extraction cannot diverge
  3. Run the parser's fuzz/unit tests after touching arity rules

Example fix

// before
if arguments.len() != 1 { bail!("line {line_number}: pick requires exactly one commit"); }
let commit = arguments.first().copied().expect("validated pick arity above");

// after: single source of truth
let Some(commit) = arguments.first().copied() else {
    bail!("line {line_number}: pick requires exactly one commit");
};
Defensive patterns

Strategy: validation

Validate before calling

// Validate the todo line shape before invoking integration if you generate it:
fn valid_pick_line(args: &[&str]) -> bool { args.len() == 1 }

Prevention

When it happens

Trigger: Parsing an `integrate-branch-upstream` todo line `pick <commit>`; the panic could only fire if the preceding arity check were removed or made inconsistent (e.g. allowing 0 arguments) — no input reaches it in the shipped code.

Common situations: Maintainers extending the todo grammar (new optional argument forms); forks that relax arity validation; none for end users.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/6283e771f8f87b2c. Report an issue: GitHub.