astral-sh/ruff · critical

Stack to never be empty

Error message

Stack to never be empty

What it means

The flake8-commas trailing-commas rule keeps a stack of bracket contexts while scanning tokens. `update_context` assumes the stack is never empty when a `For` token is processed (e.g. a comprehension's `for` inside brackets); if the stack is empty the invariant is broken and this expect panics.

Source

Thrown at crates/ruff_linter/src/rules/flake8_commas/rules/trailing_commas.rs:445

            (TokenType::Named, TokenType::Def) => Context::new(ContextType::FunctionParameters),
            (TokenType::Named | TokenType::ClosingBracket, _) => {
                Context::new(ContextType::CallArguments)
            }
            _ => Context::new(ContextType::Tuple),
        },
        TokenType::OpeningSquareBracket => match (prev.ty, prev_prev.ty) {
            (TokenType::Named, TokenType::Def | TokenType::Class | TokenType::Type) => {
                Context::new(ContextType::TypeParameters)
            }
            (TokenType::ClosingBracket | TokenType::Named | TokenType::String, _) => {
                Context::new(ContextType::Subscript)
            }
            _ => Context::new(ContextType::List),
        },
        TokenType::OpeningCurlyBracket => Context::new(ContextType::Dict),
        TokenType::Lambda => Context::new(ContextType::LambdaParameters),
        TokenType::For => {
            let last = stack.last_mut().expect("Stack to never be empty");
            *last = Context::new(ContextType::No);
            return *last;
        }
        TokenType::Comma => {
            let last = stack.last_mut().expect("Stack to never be empty");
            last.inc();
            return *last;
        }
        _ => return stack.last().copied().expect("Stack to never be empty"),
    };

    stack.push(new_context);
    new_context
}

View on GitHub (pinned to 26f38c119c)

Solutions

  1. Report the crashing file to Ruff (internal invariant violation)
  2. Upgrade/downgrade Ruff to a version without the crash
  3. Reduce/minimize the Python snippet causing the crash and avoid the construct temporarily (e.g. ignore COM rules for that file)
  4. If developing: guard with `stack.last_mut()` handling empty stacks gracefully
Defensive patterns

Strategy: fallback

Try / catch

// treat a panic in linting as a per-file failure and continue
match ruff_lint_file(path) {
    Ok(diags) => report(diags),
    Err(e) if e.is_panic() => eprintln!("skipped {}: linter crashed ({e})", path.display()),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Running lint (trailing-comma rules COM812/COM818/COM819) on token streams where a `for` token is visited before any opening bracket was pushed — only through an internal scanning bug or unbalanced brackets (e.g. parser producing tokens outside the tracked region).

Common situations: Rust contributors modifying token classification; users hitting a crash on specific Python files with unusual bracket/lambda constructs — reported as Ruff crashes rather than user 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 astral-sh/ruff@26f38c119c (2026-09-05). Data as JSON: /api/errors/6ebba676794eafd9. Report an issue: GitHub.