gleam-lang/gleam · error

Tried to reduce without 2 expressions

Error message

Tried to reduce without 2 expressions

What it means

In the expression precedence parser, `do_reduce_expression` pops two operands to combine with an operator. If fewer than two expressions are on the stack (malformed operator/operand interleaving), the parser's internal invariant is broken and it panics instead of producing a user-facing parse error.

Solutions

  1. Minimize the failing source snippet and report it as a Gleam parser bug.
  2. If patching the parser, verify each operator's handling pushes/pops operands in matched pairs.
  3. Reformat the expression with explicit parentheses as a possible workaround.
Defensive patterns

Strategy: try-catch

Try / catch

let parsed = std::panic::catch_unwind(|| parse_expression(tokens));
match parsed {
    Ok(Some(expr)) => expr,
    _ => report_compiler_bug("Tried to reduce without 2 expressions"),
}

Prevention

When it happens

Trigger: An operator reduction is triggered when the expression stack holds fewer than two `UntypedExpr` values — e.g. a binary operator encountered after an operand was consumed incorrectly or an operator handler miscounted pushes/pops.

Common situations: Parser development or regressions in Gleam's infix expression handling; essentially unreachable from ordinary user input since normal syntax errors return proper errors.

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@15b07c7830 (2026-09-14). Data as JSON: /api/errors/9a07e3bc0d6d3339. Report an issue: GitHub.

Appendix: source

Thrown at compiler-core/src/parse.rs:5134

        | Token::Macro
        | Token::Opaque
        | Token::Panic
        | Token::Pub
        | Token::Test
        | Token::Todo
        | Token::Type
        | Token::Use => None,
    }
}

/// Simple-Precedence-Parser, perform reduction for expression
fn do_reduce_expression(operator: Spanned, estack: &mut Vec<UntypedExpr>) {
    match (estack.pop(), estack.pop()) {
        (Some(right), Some(left)) => {
            let new_expression = expression_operator_reduction(operator, left, right);
            estack.push(new_expression);
        }
        _ => panic!("Tried to reduce without 2 expressions"),
    }
}

/// Simple-Precedence-Parser, perform reduction for clause guard
fn do_reduce_clause_guard(operator: Spanned, estack: &mut Vec<UntypedClauseGuard>) {
    match (estack.pop(), estack.pop()) {
        (Some(right), Some(left)) => {
            let expression = clause_guard_reduction(operator, left, right);
            estack.push(expression);
        }
        _ => panic!("Tried to reduce without 2 guards"),
    }
}

/// Simple-Precedence-Parser, perform reduction for clause guard
fn do_reduce_constant(operator: Spanned, estack: &mut Vec<UntypedConstant>) {
    match (estack.pop(), estack.pop()) {
        (Some(left), Some(right)) => {

View on GitHub (pinned to 15b07c7830)