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

Expected function or '('

Error message

Expected function or '('

What it means

Thrown by swc_css_parser while parsing a <general-enclosed> operand, most visibly in an @supports prelude (and in the media-query fallback chain). After a logical keyword, the grammar only accepts a function token such as selector(...), media(...) or supports(...), or a parenthesized block whose first non-whitespace component value is an identifier. When the current token is neither a function nor '(', the parser returns ErrorKind::Expected("function or '('") with the span of the offending token and the at-rule prelude fails to parse.

Source

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

                                ),
                            ));
                        }
                    }
                }

                if !found_ident {
                    return Err(Error::new(
                        block.span,
                        ErrorKind::Expected("ident at first position in <general-enclosed>"),
                    ));
                }

                Ok(GeneralEnclosed::SimpleBlock(block))
            }
            _ => {
                let span = self.input.cur_span();

                Err(Error::new(span, ErrorKind::Expected("function or '('")))
            }
        }
    }
}

impl<I> Parse<DocumentPreludeMatchingFunction> for Parser<I>
where
    I: ParserInput,
{
    fn parse(&mut self) -> PResult<DocumentPreludeMatchingFunction> {
        match cur!(self) {
            tok!("url") => Ok(DocumentPreludeMatchingFunction::Url(self.parse()?)),
            Token::Function {
                value: function_name,
                ..
            } => {
                if matches_eq_ignore_ascii_case!(function_name, "url", "src") {
                    Ok(DocumentPreludeMatchingFunction::Url(self.parse()?))

View on GitHub (pinned to 5176682b65)

Solutions

  1. Put every operand of the condition in parentheses: @supports (display: grid) and (display: flex) { }
  2. Use the special functions for non-declaration queries: @supports selector(a > b), @supports media(...), @supports supports(...)
  3. Delete stray tokens after and/or/not in the @supports prelude
  4. Use the reported span: it marks exactly the token that is neither a function nor '(' — search that offset in your generated CSS

Example fix

/* before */
@supports (display: grid) or 5px {
  .grid { display: grid; }
}

/* after */
@supports (display: grid) or (display: flex) {
  .grid { display: grid; }
}
Defensive patterns

Strategy: try-catch

Validate before calling

fn supports_operands_ok(css: &str) -> bool {
    let prelude = css.split_once('{').map_or(css, |(p, _)| p);
    let body = prelude.splitn(2, "supports").nth(1).unwrap_or("");
    for tok in body.split_whitespace() {
        let t = tok.trim_matches(|c| c == ',');
        if t.eq_ignore_ascii_case("and") || t.eq_ignore_ascii_case("or") || t.eq_ignore_ascii_case("not") {
            continue;
        }
        if !(t.starts_with('(') || t.ends_with(')')) {
            return false;
        }
    }
    true
}

Type guard

fn is_supports_operand_error(e: &swc_css_parser::error::Error) -> bool {
    matches!(e.kind(), swc_css_parser::error::ErrorKind::Expected(m) if *m == "function or '('")
}

Try / catch

let mut errors = Vec::new();
match parse_file::<Stylesheet>(&fm, None, config, &mut errors) {
    Ok(sheet) => handle(sheet),
    Err(err) if is_supports_operand_error(&err) => {
        let (span, _) = *err.into_inner();
        reject_css_with_location(css, span, "each @supports operand must be (...) or a function like selector(...)");
    }
    Err(err) => return Err(err.into()),
}

Prevention

When it happens

Trigger: A supports condition continued with a non-function, non-parenthesized token: '@supports (display: grid) or 5px { }', '@supports "foo" { }', '@supports 7 { }', a stray hash/number after and/or/not, or a truncated prelude where the leading '(' of the next operand was dropped (common with string concatenation of conditions).

Common situations: Hand-written @supports rules with typos, template engines or CSS-in-JS glue emitting partial condition fragments, preprocessing steps that strip or insert tokens when rewriting supports queries, and CSS copied from specs/demos that got truncated in transit.

Related errors


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