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
- Report the crashing file to Ruff (internal invariant violation)
- Upgrade/downgrade Ruff to a version without the crash
- Reduce/minimize the Python snippet causing the crash and avoid the construct temporarily (e.g. ignore COM rules for that file)
- 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
- Pin a Ruff version known to pass COM tests on your corpus
- Minimize and report any crashing file upstream
- Keep COM rules isolated in config so they can be disabled quickly
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
- Expected `NamedTuple` definition r.h.s. to be a call express
- PySourceType always parses into a module
- module to include at least one segment
- storage-free owned constraint sets must have terminal roots
- dynamic class definitions should only be used for assignment
AI-assisted analysis of astral-sh/ruff@26f38c119c (2026-09-05).
Data as JSON: /api/errors/6ebba676794eafd9.
Report an issue: GitHub.