gleam-lang/gleam · error

wrong number of subjects

Error message

wrong number of subjects

What it means

This panic comes from an `.expect("wrong number of subjects")` inside the exhaustiveness (pattern-matching) compiler of the Gleam compiler core. When compiling a `case` expression's branches, each pattern list is zipped against the pre-registered subject variables; the length mismatch means a branch had more patterns than there were case subjects, which the typechecker should have rejected earlier. It is an internal invariant assertion, not a user-facing error.

Solutions

  1. Check the number of patterns in each clause matches the number of case subjects before registration; the parser/typechecker should report 'case clause has too many patterns' instead
  2. Verify the code that builds `subject_variables` pushes one variable per subject (look at where `Compiler::new` is called)
  3. Run with a debug build to get the full panic backtrace and identify which pass produced the oversized pattern vector
  4. If you are a library user (not compiler dev), this is a compiler bug — file an issue with the code that triggers it

Example fix

// before
let var = self.subject_variables.get(i).expect("wrong number of subjects");
// after
let var = self.subject_variables.get(i).unwrap_or_else(|| {
    panic!("clause has {} patterns but {} subjects were registered", patterns.len(), self.subject_variables.len())
});
Defensive patterns

Strategy: validation

Validate before calling

if patterns.len() != subjects.len() {
    return Err(Error::CaseClauseArityMismatch { patterns: patterns.len(), subjects: subjects.len() });
}

Type guard

fn clause_matches_subjects(patterns: &[TypedPattern], subjects: &[Variable]) -> bool {
    patterns.len() == subjects.len()
}

Prevention

When it happens

Trigger: Calling `Compiler::compile_body` (or the clause-registration path) with a `patterns` iterator/Vec longer than `subject_variables` — e.g. an earlier AST pass failed to keep pattern count equal to the number of case subjects for a multi-subject case clause.

Common situations: Hitting this while hacking on the Gleam compiler: adding a new pattern desugaring that duplicates patterns, miscounting subjects in a rewrite pass, or fuzzing the frontend with malformed case expressions that bypass the typechecker's arity check.

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


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

Appendix: source

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

    /// 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 single
    /// pattern without having a fully fledged `TypedClause` to pass to the `add_clause`
    /// method. For example, in `let` destructurings.
    ///
    pub fn add_pattern(&mut self, pattern: &TypedPattern) {

View on GitHub (pinned to 49f8762da5)