facebook/flow · error
Popping lex mode from empty stack
Error message
Popping lex mode from empty stack
What it means
The lexer mode stack tracks nested lexical contexts: push_lex_mode pushes a LexMode and resets the lookahead; pop_lex_mode (rust_port/crates/flow_parser/src/parser_env.rs:2549) pops and expects the stack to be non-empty. This panic is a stack underflow: the parser popped more lex modes than it pushed. Because double_pop_lex_mode pops twice, the same message can originate from three adjacent lines (2551, 2559, 2562).
Source
Thrown at rust_port/crates/flow_parser/src/parser_env.rs:2562
/* [maybe env t] eats the next token and returns [true] if it is [t], else return [false] */
pub(crate) fn maybe(env: &mut ParserEnv, t: TokenKind) -> Result<bool, Rollback> {
let is_t = peek::token(env) == &t;
if is_t {
token(env)?;
}
Ok(is_t)
}
pub(crate) fn push_lex_mode(env: &mut ParserEnv, mode: LexMode) {
env.lex_mode_stack.push(mode);
let new_lex_mode = env.lex_mode();
env.lookahead.reset(new_lex_mode);
}
pub(crate) fn pop_lex_mode(env: &mut ParserEnv) {
env.lex_mode_stack
.pop()
.expect("Popping lex mode from empty stack");
let new_lex_mode = env.lex_mode();
env.lookahead.reset(new_lex_mode);
}
pub(crate) fn double_pop_lex_mode(env: &mut ParserEnv) {
env.lex_mode_stack
.pop()
.expect("Popping lex mode from empty stack");
env.lex_mode_stack
.pop()
.expect("Popping lex mode from empty stack");
let new_lex_mode = env.lex_mode();
env.lookahead.reset(new_lex_mode);
}
pub(crate) fn rescan_as_template_from<'a>(
env: &mut ParserEnv<'a>,
prev_cursor: wrapped_lex_env::WrappedLexCursor,View on GitHub (pinned to 5c86586199)
Solutions
- Capture the exact panicking input and stack trace; identify the parse rule owning the pop_lex_mode call site
- Audit that rule for a push_lex_mode without a matching pop on every path, especially error/rollback paths
- Compare against upstream Flow parser behavior on the same input to see which side is unbalanced
- Upgrade flow_parser; lexer-mode balancing regressions are usually fixed quickly after fuzz reports
- If you maintain the code, use a checked pop that records an internal parse error instead of panicking
Example fix
// before: blind pop panics on underflow
pub(crate) fn pop_lex_mode(env: &mut ParserEnv) {
env.lex_mode_stack.pop().expect("Popping lex mode from empty stack");
let new_lex_mode = env.lex_mode();
env.lookahead.reset(new_lex_mode);
}
// after: checked pop surfaces an internal error instead of crashing
pub(crate) fn pop_lex_mode(env: &mut ParserEnv) {
if env.lex_mode_stack.pop().is_none() {
env.error_at_current_loc(ParseError::Internal("lex mode stack underflow"));
return;
}
let new_lex_mode = env.lex_mode();
env.lookahead.reset(new_lex_mode);
} Defensive patterns
Strategy: try-catch
Try / catch
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| parse(src)));
if result.is_err() {
// record input for a parser bug report; skip file, keep the batch alive
report_and_skip(file);
} Prevention
- Fuzz the parser on upgrades, especially with template literals, JSX, and type annotations
- If maintaining parser rules, assert lex_mode_stack depth in debug builds around push/pop pairs
- Keep panicking inputs as regression tests
When it happens
Trigger: A parse path where pop_lex_mode runs without a matching push_lex_mode: typically an error-recovery branch that pops after the success path already popped, or a rescan path (rescan_as_template_from) that rewinds the lexer cursor without restoring the mode stack. Line 2551 is pop_lex_mode itself.
Common situations: Unbalanced push/pop introduced while porting or refactoring parser rules; inputs mixing template literals, JSX, or type annotations that exercise rescanning; fuzzing the parser with malformed nested syntax.
Related errors
- Internal Error: Tried to add_declared_private with outside o
- Unknown exception reading from the server: {}
- Error sending command to server: {}
- invalid line
- invalid column
AI-assisted analysis of facebook/flow@5c86586199 (2026-08-20).
Data as JSON: /api/errors/404b0344391c91a6.
Report an issue: GitHub.