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

Expected ident (exclude the keywords 'only', 'not', 'and', '

Error message

Expected ident (exclude the keywords 'only', 'not', 'and', 'or' and 'layer')

What it means

Thrown while parsing a <media-type> inside an @media (or @import ... media) prelude. The media type must be a plain identifier, and the five words 'only', 'not', 'and', 'or', 'layer' are grammar keywords that cannot serve as the type itself. When one of those keywords (or a non-ident token) sits where the media type is expected, ErrorKind::Expected("ident (exclude the keywords 'only', 'not', 'and', 'or' and 'layer')") is raised.

Source

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

        })
    }
}

impl<I> Parse<MediaType> for Parser<I>
where
    I: ParserInput,
{
    fn parse(&mut self) -> PResult<MediaType> {
        match cur!(self) {
            _ if !is_one_of_case_insensitive_ident!(self, "not", "and", "or", "only", "layer") => {
                let name: Ident = self.parse()?;

                Ok(MediaType::Ident(name))
            }
            _ => {
                let span = self.input.cur_span();

                Err(Error::new(
                    span,
                    ErrorKind::Expected(
                        "ident (exclude the keywords 'only', 'not', 'and', 'or' and 'layer')",
                    ),
                ))
            }
        }
    }
}

impl<I> Parse<MediaCondition> for Parser<I>
where
    I: ParserInput,
{
    fn parse(&mut self) -> PResult<MediaCondition> {
        let start_pos = self.input.cur_span().lo;
        let mut last_pos;
        let mut conditions = Vec::new();

View on GitHub (pinned to 5176682b65)

Solutions

  1. Follow every only/not modifier with a real media type: @media only screen { }, @media not print { }
  2. Remove dangling and/or keywords at the end of a query list item
  3. Use @layer or @import ... layer() for cascade layers — 'layer' is never a media type
  4. Prefer level-4 pure feature queries like @media (width >= 600px) when no media type is needed

Example fix

/* before */
@media only {
  .x { color: red; }
}

/* after */
@media only screen {
  .x { color: red; }
}
Defensive patterns

Strategy: validation

Validate before calling

const RESERVED: [&str; 5] = ["only", "not", "and", "or", "layer"];

fn media_type_slot_ok(query: &str) -> bool {
    let mut toks = query.split_whitespace();
    while let Some(t) = toks.next() {
        let t = t.trim_matches(|c| c == ',');
        if t.eq_ignore_ascii_case("only") || t.eq_ignore_ascii_case("not") {
            match toks.next() {
                Some(next) if RESERVED.iter().any(|k| next.eq_ignore_ascii_case(k)) => return false,
                Some(_) => {}
                None => return false,
            }
        }
    }
    true
}

Type guard

fn is_media_type_error(e: &swc_css_parser::error::Error) -> bool {
    matches!(e.kind(), swc_css_parser::error::ErrorKind::Expected(m) if *m == "ident (exclude the keywords 'only', 'not', 'and', 'or' and 'layer')")
}

Try / catch

match parse_file::<Stylesheet>(&fm, None, config, &mut errors) {
    Ok(sheet) => handle(sheet),
    Err(err) if is_media_type_error(&err) => {
        let (span, _) = *err.into_inner();
        return Err(fixup_report(css, span, "supply a real media type (screen, print, all) after only/not"));
    }
    Err(err) => return Err(err.into()),
}

Prevention

When it happens

Trigger: '@media only { }' and '@media not { }' (modifier with no media type after it), '@media and { }', '@media layer { }' (using the cascade-layer keyword where a type belongs), or a query like '@media screen and only' where a keyword appears where the next type or condition must start.

Common situations: Dynamically composed media queries where an optional type segment was omitted and only/not is left dangling, migrations from CSS2 media types, and authors experimenting with @media layer(...) variants after learning cascade layers.

Related errors


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