swc-project/swc · error
failed to parse `{}` using lexical: {:?}
Error message
failed to parse `{}` using lexical: {:?} What it means
This code parses the An+B microsyntax (`:nth-child(2n-3)`). In the branch for `n-<digits>` forms it feeds the digits after 'n-' to lexical's i32 parser inside unwrap_or_else(unreachable!) — the tokenizer already validated the shape, so failure means the digits are grammatically valid but overflow i32 (|B| beyond 2,147,483,647).
Source
Thrown at crates/swc_css_parser/src/parser/selectors/mod.rs:1389
};
b = Some(b_sign * number.0 as i32);
let mut b_raw_str = String::new();
b_raw_str.push(' ');
b_raw_str.push(b_sign_raw);
b_raw_str.push(' ');
b_raw_str.push_str(&number.1);
b_raw = Some(self.input.atom(b_raw_str));
}
// '+'? <ndashdigit-ident>
// <dashndashdigit-ident>
// <ndashdigit-dimension>
_ if dash_after_n == Some('-') => {
let b_from_ident = &n_value[2..];
let parsed: i32 = lexical::parse(b_from_ident).unwrap_or_else(|err| {
unreachable!(
"failed to parse `{}` using lexical: {:?}",
b_from_ident, err
)
});
b = Some(-parsed);
let mut b_raw_str = String::new();
b_raw_str.push('-');
b_raw_str.push_str(b_from_ident);
b_raw = Some(self.input.atom(b_raw_str));
}
// '+'? n
// -n
_ if dash_after_n.is_none() => {}
_ => {View on GitHub (pinned to 5176682b65)
Solutions
- Fix the selector: keep the B term of An+B within signed 32-bit range (usually `2n` or small offsets suffice)
- Sanitize generated CSS before parsing: reject :nth-* selectors whose B component does not fit i32
- Report the selector upstream — swc should return a parse error instead of panicking
Example fix
/* before */
li:nth-child(2n-99999999999) { color: red }
/* after */
li:nth-child(2n) { color: red } Defensive patterns
Strategy: validation
Validate before calling
// Reject :nth-* selectors whose B term overflows i32 before parsing
use regex::Regex;
fn nth_b_in_range(css: &str) -> bool {
let re = Regex::new(
r#":nth(?:-last)?-(?:child|of-type)\(\s*[+-]?\d*n[^)]*?([+-]\s*\d+)"#,
).unwrap();
re.captures_iter(css).all(|c| {
c[1].replace(' ', "").parse::<i32>().map(|_| true).unwrap_or(false)
})
}
assert!(nth_b_in_range(&css), "nth-child B term exceeds i32"); Try / catch
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| parse_css(&css))) {
Ok(v) => v,
Err(p) if panic_message(&p).contains("lexical") => {
// fallback: strip/rewrite the oversized :nth-* selector and reparse
}
Err(p) => std::panic::resume_unwind(p),
} Prevention
- Keep An+B offsets small and meaningful; huge B values are almost always generation bugs
- Sanitize machine-generated selectors before handing CSS to the parser
- Pin i32 bounds in your CSS generator's nth rules
When it happens
Trigger: Parsing a selector like `:nth-child(3n-99999999999)` or `:nth-last-of-type(2n+3000000000)` where the B component exceeds the signed 32-bit range.
Common situations: Machine-generated CSS (utility frameworks, spreadsheet/export tools) emitting enormous nth offsets; fuzzing corpora; minified third-party stylesheets with pathological selectors.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- internal error: entered unreachable code
- internal error: entered unreachable code
- internal error: entered unreachable code
- Cannot transform sum children into CalcProduct
- Cannot transform product children into CalcProduct
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/595b3d7b11f0844c.
Report an issue: GitHub.