gleam-lang/gleam · error

at least one choice

Error message

at least one choice

What it means

Panic from `choices.pop().expect("at least one choice")` while splitting a decision tree into a final `FallbackCheck::RuntimeCheck`. The algorithm assumes that when there is no catch-all fallback there is at least one remaining choice to promote to the final check; an empty choices vec breaks that invariant.

Solutions

  1. Guard with an explicit `match choices.pop()` and fall back to `FallbackCheck::CatchAll` when the list is empty instead of panicking.
  2. Trace why `choices` is empty: add a debug assert before the split logging the choice count for the failing pattern.
  3. Fix the upstream pruning step so it never removes the last non-catch-all check without producing a catch-all fallback.

Example fix

// before
let (last_check, last_choice) = choices.pop().expect("at least one choice");
// after
let Some((last_check, last_choice)) = choices.pop() else {
    let fallback = FallbackCheck::CatchAll { ignored_checks: Vec::new() };
    return (choices, splitter.fallback, fallback);
};
Defensive patterns

Strategy: validation

Validate before calling

if choices.is_empty() {
    // use catch-all fallback instead of promoting the last choice
}

Type guard

fn has_final_choice(choices: &[Choice]) -> bool { !choices.is_empty() }

Try / catch

std::panic::catch_unwind(|| split_node(node)).unwrap_or_else(|_| fallback_to_catch_all(node))

Prevention

When it happens

Trigger: Compiling a case expression where the choice list for a branch node was fully consumed (or was empty from the start) before this split, e.g. after a bug in overlap removal popped every choice, or a branch reached this code with only a catch-all.

Common situations: Compiler developers hit this after modifying how `remaining_choices`/overlaps prune checks, or on pathological case expressions where all checks were deduplicated away.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of gleam-lang/gleam@49f8762da5 (2026-09-14). Data as JSON: /api/errors/93936bc32d1bda82. Report an issue: GitHub.

Appendix: source

Thrown at compiler-core/src/exhaustiveness.rs:2646

                // matched on then we want to ignore all of those and clump them
                // into a single "fallback" branch to avoid bloating the generated
                // tree.
                let mut ignored_checks = vec![];
                let mut remaining_choices = vec![];
                for choice in choices.into_iter() {
                    if choice.0.is_ignored() {
                        ignored_checks.push(choice.0);
                    } else {
                        remaining_choices.push(choice);
                    }
                }

                let fallback_check = FallbackCheck::CatchAll { ignored_checks };
                (remaining_choices, splitter.fallback, fallback_check)
            } else {
                // Otherwise we just use the last check as the final one that
                // can be outright skipped.
                let (last_check, last_choice) = choices.pop().expect("at least one choice");
                let fallback_check = FallbackCheck::RuntimeCheck {
                    check: Box::new(last_check),
                };
                (choices, last_choice, fallback_check)
            };

        let choices = self.compile_all_choices(choices);
        let fallback = Box::new(self.compile(fallback));
        Decision::Switch {
            var,
            choices,
            fallback,
            fallback_check,
        }
    }

    fn compile_all_choices(
        &mut self,

View on GitHub (pinned to 49f8762da5)