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

  1. Fix the selector: keep the B term of An+B within signed 32-bit range (usually `2n` or small offsets suffice)
  2. Sanitize generated CSS before parsing: reject :nth-* selectors whose B component does not fit i32
  3. 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

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

Related errors


AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17). Data as JSON: /api/errors/595b3d7b11f0844c. Report an issue: GitHub.