gleam-lang/gleam · error

unknown variant index

Error message

unknown variant index

What it means

Panic when matching a `RuntimeCheckKind::Variant { index }` against a `BranchMode::NamedType`: `constructors.get(index)` returned None. The variant index recorded in the check must be valid for the constructors of the named type at that node; if not, the check and the branch were built from different types.

Solutions

  1. Rebuild the Variant check from the current branch's constructors instead of carrying a stale positional index.
  2. Verify the subject type used to build the RuntimeCheckKind is the same NamedType stored in BranchMode::NamedType.
  3. Return an unreachable/bug error instead of panicking, and reduce the failing module to a minimal repro to file upstream.

Example fix

// before
constructors.get(index).expect("unknown variant index")
// after
constructors.get(index).unwrap_or_else(|| {
    panic!("variant index {} invalid for {} constructors", index, constructors.len())
})
Defensive patterns

Strategy: validation

Validate before calling

assert!(index < constructors.len(), "variant index {} invalid for this branch", index);

Type guard

fn constructor_for(constructors: &[Constructor], index: usize) -> Option<&Constructor> {
    constructors.get(index)
}

Try / catch

std::panic::catch_unwind(|| compile_check(check)).map_err(|_| Bug::UnknownVariantIndex(index))

Prevention

When it happens

Trigger: Compiling a variant check whose `index` exceeds the constructor count of the branch's NamedType — typically after a type substitution changed the subject's type, or a check was built for a different variant-bearing type and reused at the wrong node.

Common situations: Seen by Gleam compiler contributors after changes to type inference/unification feeding exhaustiveness, or when custom types are reordered between check construction and tree compilation.

Related errors


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

Appendix: source

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

        &mut self,
        kind: RuntimeCheckKind,
        branch_mode: &BranchMode,
    ) -> RuntimeCheck {
        match (kind, branch_mode) {
            (RuntimeCheckKind::Int { int_value }, _) => RuntimeCheck::Int { int_value },
            (RuntimeCheckKind::Float { float_value }, _) => RuntimeCheck::Float { float_value },
            (RuntimeCheckKind::String { value }, _) => RuntimeCheck::String { value },
            (RuntimeCheckKind::StringPrefix { prefix }, _) => RuntimeCheck::StringPrefix {
                prefix,
                rest: self.fresh_variable(string()),
            },
            (RuntimeCheckKind::Tuple { .. }, BranchMode::Tuple { elements }) => {
                self.is_tuple_check(elements)
            }
            (RuntimeCheckKind::Variant { index }, BranchMode::NamedType { constructors, .. }) => {
                self.is_variant_check(
                    index,
                    constructors.get(index).expect("unknown variant index"),
                )
            }
            (RuntimeCheckKind::EmptyList, _) => RuntimeCheck::EmptyList,
            (RuntimeCheckKind::NonEmptyList, BranchMode::List { inner_type }) => {
                self.is_list_check(inner_type.clone())
            }
            (_, _) => unreachable!("type checking should make this impossible"),
        }
    }

    /// Comes up with new pattern cecks that have to match in case a given
    /// runtime check succeeds for the given pattern.
    ///
    /// Let's make an example: when we have a pattern - say `a is Wibble(1, [])` -
    /// we come up with a runtime check to perform on it. For our example the
    /// runtime check is to make sure that `a` is indeed a `Wibble` variant.
    /// However, after successfully performing that check we're left with much to
    /// do! We know that `a` is `Wibble` but now we'll have to make sure that its

View on GitHub (pinned to 49f8762da5)