swc-project/swc · error · swc_css_parser::error::Error

Expected 'left', 'right', 'first' or 'blank' ident

Error message

Expected 'left', 'right', 'first' or 'blank' ident

What it means

Thrown while parsing a @page pseudo page selector. The CSS paged-media grammar allows only the four pseudo pages :left, :right, :first and :blank; when the ident after ':' is none of them (or the token is not an ident), ErrorKind::Expected("'left', 'right', 'first' or 'blank' ident") is raised at that token.

Source

Thrown at crates/swc_css_parser/src/parser/at_rules/mod.rs:2041

    I: ParserInput,
{
    fn parse(&mut self) -> PResult<PageSelectorPseudo> {
        let span = self.input.cur_span();

        expect!(self, ":");

        let value = match cur!(self) {
            Token::Ident { value, .. }
                if matches_eq_ignore_ascii_case!(value, "left", "right", "first", "blank") =>
            {
                let name: Ident = self.parse()?;

                name
            }
            _ => {
                let span = self.input.cur_span();

                return Err(Error::new(
                    span,
                    ErrorKind::Expected("'left', 'right', 'first' or 'blank' ident"),
                ));
            }
        };

        Ok(PageSelectorPseudo {
            span: span!(self, span.lo),
            value,
        })
    }
}

impl<I> Parse<LayerName> for Parser<I>
where
    I: ParserInput,
{
    fn parse(&mut self) -> PResult<LayerName> {

View on GitHub (pinned to 5176682b65)

Solutions

  1. Use only @page :left, @page :right, @page :first, @page :blank
  2. Use named pages for custom page setups: @page narrow { size: A5; } plus 'page: narrow' on elements
  3. Drop unsupported pseudo pages before feeding print CSS to swc

Example fix

/* before */
@page :center {
  margin: 2cm;
}

/* after */
@page {
  margin: 2cm;
}
Defensive patterns

Strategy: validation

Validate before calling

const PAGE_PSEUDOS: [&str; 4] = ["left", "right", "first", "blank"];

fn page_pseudos_ok(css: &str) -> bool {
    for line in css.lines() {
        let t = line.trim();
        if t.starts_with("@page") {
            for part in t[5..].split_whitespace() {
                let p = part.trim_matches(|c| c == ',' || c == '{' || c == '}');
                if let Some(name) = p.strip_prefix(':') {
                    if !PAGE_PSEUDOS.iter().any(|k| name.eq_ignore_ascii_case(k)) {
                        return false;
                    }
                }
            }
        }
    }
    true
}

Type guard

fn is_page_pseudo_error(e: &swc_css_parser::error::Error) -> bool {
    matches!(e.kind(), swc_css_parser::error::ErrorKind::Expected(m) if *m == "'left', 'right', 'first' or 'blank' ident")
}

Try / catch

match parse_file::<Stylesheet>(&fm, None, config, &mut errors) {
    Ok(sheet) => handle(sheet),
    Err(err) if is_page_pseudo_error(&err) => {
        let (span, _) = *err.into_inner();
        hint_at_span(css, span, "@page accepts only :left, :right, :first, :blank — use named pages for custom setups");
    }
    Err(err) => return Err(err.into()),
}

Prevention

When it happens

Trigger: '@page :center { }', '@page :nth(2) { }', '@page :recto { }' — any pseudo not in the allowed set.

Common situations: Authors assuming nth() page selectors or writing custom pseudo names, print-stylesheet generators exposing arbitrary pseudo input, and porting drafts (recto/verso) that engines never shipped in this form.

Related errors


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