gleam-lang/gleam · error
pattern index
Error message
pattern index
What it means
Panic while registering all subject patterns of a multi-subject case expression: `patterns.get(i)` returned None for index i. The surrounding loop iterates `0..patterns.len()` against the same vec, so a failure means `patterns` was mutated during iteration or the loop bounds came from a different collection.
Solutions
- Make sure `self.register` does not mutate or replace `self.patterns` during the loop (take a snapshot or use an iterator over the original vec).
- Use `patterns.iter().zip(subject_variables.iter())` with collected results instead of index-based iteration to avoid shared-borrow workarounds.
- Add a debug assert that `patterns.len()` matches `subject_variables.len()` before the loop (the adjacent 'wrong number of subjects' expect covers the other side).
Example fix
// before
let pattern = self.register(patterns.get(i).expect("pattern index"));
// after
let Some(p) = patterns.get(i) else {
panic!("pattern index {} out of {} patterns", i, patterns.len())
};
let pattern = self.register(p); Defensive patterns
Strategy: validation
Validate before calling
assert_eq!(patterns.len(), subject_variables.len(), "subjects and variables out of sync");
for (pattern, var) in patterns.iter().zip(subject_variables.iter()) { /* ... */ } Type guard
fn paired_subjects<'a>(p: &'a [Id<Pattern>], v: &'a [Variable]) -> Option<std::vec::Vec<(&'a Id<Pattern>, &'a Variable)>> {
if p.len() == v.len() { Some(p.iter().zip(v.iter()).collect()) } else { None }
} Try / catch
std::panic::catch_unwind(|| register_subjects(patterns)).map_err(|_| Bug::PatternIndexOutOfRange)
Prevention
- Avoid index loops over vecs that `register` may mutate; use iterators or snapshots.
- Keep subject pattern and variable collections in lockstep length.
- Never trigger re-entrant registration inside a loop over the same store.
When it happens
Trigger: Compiling a case expression with multiple subjects where `register` (called inside the loop) recursively triggers re-entry that drains/mutates `patterns`, or where the loop's `patterns.len()` and the vec being indexed are different collections that diverged.
Common situations: Gleam compiler contributors hit this when adding side effects inside `register` that mutate the pattern store, or after refactoring subject pattern collection so the vec is rebuilt mid-loop.
Related errors
- no unconditional patterns left
- 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/a9fa8c55a2508fc7.
Report an issue: GitHub.
Appendix: source
Thrown at compiler-core/src/exhaustiveness.rs:3660
}
/// Registers a `TypedClause` as one of the branches to be compiled.
///
/// If you don't have a clause and just have a simple `TypedPattern` you want
/// to generate a decision tree for you can use `add_pattern`.
///
pub fn add_clause(&mut self, branch: &TypedClause) {
let all_patterns =
std::iter::once(&branch.pattern).chain(branch.alternative_patterns.iter());
for (alternative_index, patterns) in all_patterns.enumerate() {
let mut checks = Vec::with_capacity(patterns.len());
// We're doing the zipping ourselves instead of using iters.zip as the
// borrow checker would complain and the only workaround would be to
// allocate an entire new vector each time.
for i in 0..patterns.len() {
let pattern = self.register(patterns.get(i).expect("pattern index"));
let var = self
.subject_variables
.get(i)
.expect("wrong number of subjects");
checks.push(var.is(pattern));
}
let has_guard = branch.guard.is_some();
let branch = Branch::new(self.number_of_clauses, alternative_index, checks, has_guard);
self.branches.push(branch);
}
self.number_of_clauses += 1;
}
/// Add a single pattern as a branch to be compiled.
///
/// This is useful in case one wants to check exhaustiveness of a singleView on GitHub (pinned to 49f8762da5)