swc-project/swc · error · Error

'none' value of an ident token

Error message

'none' value of an ident token

What it means

Thrown by swc_css_parser while parsing the FIRST channel of an rgb()/rgba() function (crates/swc_css_parser/src/parser/values_and_units/mod.rs:415-428). When that channel is an identifier, CSS Color 4 allows only the keyword 'none' (matched with eq_ignore_ascii_case); any other word is a grammar violation and the parser returns ErrorKind::Expected("'none' value of an ident token") with the span of the offending ident. Note this ident arm is unconditional, so it fires even in comma-style legacy syntax like rgb(red, 0, 0). The error only fires on literal tokens: if a var()/env()/constant() was consumed earlier, try_parse_variable_function returns Ok(None) instead of erroring.

Source

Thrown at crates/swc_css_parser/src/parser/values_and_units/mod.rs:423

                    "rgb" | "rgba" => {
                        let percentage_or_number_or_none = self.try_parse_variable_function(
                            |parser, has_variable_before| match cur!(parser) {
                                tok!("percentage") => {
                                    Ok(Some(ComponentValue::Percentage(parser.parse()?)))
                                }
                                tok!("number") => Ok(Some(ComponentValue::Number(parser.parse()?))),
                                Token::Function { value, .. } if is_math_function(value) => {
                                    Ok(Some(ComponentValue::Function(parser.parse()?)))
                                }
                                tok!("ident") => {
                                    is_legacy_syntax = false;

                                    let ident: Box<Ident> = parser.parse()?;

                                    if ident.value.eq_ignore_ascii_case("none") {
                                        Ok(Some(ComponentValue::Ident(ident)))
                                    } else {
                                        Err(Error::new(
                                            ident.span,
                                            ErrorKind::Expected("'none' value of an ident token"),
                                        ))
                                    }
                                }
                                _ => {
                                    if !has_variable_before {
                                        Err(Error::new(
                                            parser.input.cur_span(),
                                            ErrorKind::Expected(
                                                "percentage, number, function (math functions) or \
                                                 ident (with 'none' value) token",
                                            ),
                                        ))
                                    } else {
                                        Ok(None)
                                    }
                                }

View on GitHub (pinned to 5176682b65)

Solutions

  1. Replace the ident channel with a numeric channel: 'color: rgb(red 0 0)' -> 'color: rgb(255 0 0)', or just write 'color: red' since the keyword is already a valid color.
  2. If you want keyword-derived channels, use relative color syntax: 'rgb(from red r g b)'.
  3. For dynamic values pass them through var(): 'rgb(var(--ch) 0 0)' — the parser skips the strict grammar once a var() is seen.
  4. Read err.kind()/err.message() and the span from err.into_inner().0 to pinpoint the exact ident in the source file.

Example fix

/* before */
color: rgb(red 0 0);
color: rgb(red, 0, 0);

/* after */
color: red;
color: rgb(255 0 0);
color: rgb(from red r g b);
Defensive patterns

Strategy: try-catch

Validate before calling

fn rgb_first_channel_ok(arg: &str) -> bool {
    let a = arg.trim();
    a.eq_ignore_ascii_case("none")
        || a.ends_with('%')
        || a.parse::<f64>().is_ok()
        || a.starts_with("var(")
        || a.starts_with("env(")
        || ["calc","min","max","clamp"].iter().any(|f| a.starts_with(f))
}
// before emitting generated CSS:
assert!(rgb_first_channel_ok(ch), "rgb() first channel must be number/percentage/none, got {ch}");

Type guard

fn is_valid_none_ident(ident: &swc_css_ast::Ident) -> bool {
    ident.value.eq_ignore_ascii_case("none")
}

Try / catch

let parsed = swc_css_parser::parse_string::<swc_css_ast::Stylesheet>(css, config);
if let Err(err) = parsed {
    if matches!(&err.kind(), swc_css_parser::error::ErrorKind::Expected(m) if m.contains("'none' value of an ident token")) {
        let (span, _) = err.into_inner();
        // report: rgb()/rgba() channel is a keyword; only 'none' is allowed, span points at it
    }
    return Err(err.into());
}

Prevention

When it happens

Trigger: Calling swc_css_parser::parse_string (directly or via an swc-based minifier/transform) on CSS whose first rgb()/rgba() argument is a word that is not 'none': 'color: rgb(red 0 0)', 'rgb(red, 0, 0)', 'rgba(primary 25 50)'. The tok!("ident") arm at line 415 sets is_legacy_syntax = false, parses the ident, and errors at ident.span when the value check at line 420 fails.

Common situations: Using a named color inside rgb() instead of as the color value itself; template interpolation that inserts a variable name ('primary') instead of a number into a generated rgb() string; porting Sass/older tooling that tolerated keyword channels; hand-written design-token CSS pasted from specs that use lab/oklch-style keyword examples.

Related errors


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