gleam-lang/gleam · error
no unconditional patterns left
Error message
no unconditional patterns left
What it means
Panic from `pattern.to_runtime_check_kind().expect("no unconditional patterns left")`. `to_runtime_check_kind` returns None for patterns that still contain unprocessed sub-checks (e.g. guards or nested patterns that cannot be a single unconditional check); this code assumed every pattern reaching it had been fully reduced to one runtime check kind.
Solutions
- Extend `to_runtime_check_kind` to handle the new pattern shape, or ensure the pattern is fully simplified before reaching this code.
- Check the caller's fast paths (bit-array/list handling above this line) cover every compound pattern kind.
- Add a test with the pattern that panics and fix the reduction pass so it never forwards unsimplified patterns.
Example fix
// before
let kind = pattern.to_runtime_check_kind().expect("no unconditional patterns left");
// after
let kind = pattern.to_runtime_check_kind().unwrap_or_else(|| {
panic!("pattern {:?} did not reduce to a runtime check kind", pattern)
}); Defensive patterns
Strategy: validation
Validate before calling
// Only call when the pattern is a simple unconditional pattern:
if pattern.to_runtime_check_kind().is_none() { simplify_pattern(pattern)?; } Type guard
fn is_unconditional(pattern: &Pattern) -> bool {
pattern.to_runtime_check_kind().is_some()
} Try / catch
std::panic::catch_unwind(|| compile_pattern(p)).unwrap_or_else(|_| Err(Error::UnsupportedPattern(p.clone())))
Prevention
- Extend to_runtime_check_kind whenever a new pattern kind is added.
- Run the reduction pass exhaustively before compiling patterns.
- Add a compile-fail/unit test for each new pattern shape.
When it happens
Trigger: Compiling a case branch whose pattern is not yet a simple unconditional pattern — e.g. a pattern with a guard, or a nested/compound pattern that the earlier reduction step failed to split — reaching `compile_pattern`/`add_branch` and calling `to_runtime_check_kind`.
Common situations: Compiler developers hit this when adding new pattern forms (e.g. new literal or bit-array segment types) without teaching `to_runtime_check_kind` (or the reduction pass) about them.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- pattern index
- at least one choice
- bit array compilation with no choice
- check to already be a choice
- empty bit array test
AI-assisted analysis of gleam-lang/gleam@49f8762da5 (2026-09-14).
Data as JSON: /api/errors/c24aecfacdf0caaf.
Report an issue: GitHub.
Appendix: source
Thrown at compiler-core/src/exhaustiveness.rs:3122
pattern_check: PatternCheck,
mut branch: Branch,
branch_mode: &BranchMode,
compiler: &mut Compiler<'_>,
) {
let pattern = compiler.pattern(pattern_check.pattern).clone();
// Bit array patterns are split in a different way that requires special
// handling. Instead of reasoning on overlapping checks what we do it we
// always split the decision tree in two distinct paths based on one of
// the bit array pattern's tests.
if let Pattern::BitArray { tests } = pattern {
self.add_checked_bit_array_branch(pattern_check, tests, branch, compiler);
return;
}
let kind = pattern
.to_runtime_check_kind()
.expect("no unconditional patterns left");
let CheckOverlaps {
overlapping,
needs_new_choice,
} = self.check_overlaps(&kind);
// The branch is relevant to every existing choice its check overlaps
// with, so we add it (together with any newly discovered checks) to all
// of those paths. For a string prefix this also includes any longer
// prefixes and exact literals that start with it. When one of those
// matched but its branch then failed, control falls through to this
// shorter prefix.
for index in overlapping.iter() {
let (overlapping_check, branches) = self
.choices
.get_mut(*index)
.expect("check to already be a choice");
View on GitHub (pinned to 49f8762da5)