swc-project/swc · error · swc_css_parser::error::Error
Expected left comparison operator should be equal right comp
Error message
Expected left comparison operator should be equal right comparison operator
What it means
Validation applied after parsing a range interval '(a OP1 name OP2 c)': both comparisons must point the same way — OP1 and OP2 must both come from {<, <=} or both from {>, >=}. Mixing directions (for example <= with >=) produces ErrorKind::Expected("left comparison operator should be equal right comparison operator"). The span is the whole feature, so the message plus span identifies which interval is inconsistent.
Source
Thrown at crates/swc_css_parser/src/parser/at_rules/mod.rs:1857
let is_valid_operator = match left_comparison {
MediaFeatureRangeComparison::Lt | MediaFeatureRangeComparison::Le
if right_comparison == MediaFeatureRangeComparison::Lt
|| right_comparison == MediaFeatureRangeComparison::Le =>
{
true
}
MediaFeatureRangeComparison::Gt | MediaFeatureRangeComparison::Ge
if right_comparison == MediaFeatureRangeComparison::Gt
|| right_comparison == MediaFeatureRangeComparison::Ge =>
{
true
}
_ => false,
};
if !is_valid_operator {
return Err(Error::new(
span,
ErrorKind::Expected(
"left comparison operator should be equal right comparison operator",
),
));
}
Ok(MediaFeature::RangeInterval(MediaFeatureRangeInterval {
span: span!(self, span.lo),
left: Box::new(left),
left_comparison,
name,
right_comparison,
right,
}))
}
_ => Err(Error::new(span, ErrorKind::Expected("identifier value"))),
}View on GitHub (pinned to 5176682b65)
Solutions
- Make both operators the same direction: @media (400px <= width <= 700px)
- Or split the constraint into two conjoined features: @media (min-width: 400px) and (max-width: 700px)
- When generating intervals, derive the second operator from the first instead of a separate variable
Example fix
/* before */
@media (400px <= width >= 700px) { }
/* after */
@media (400px <= width <= 700px) { } Defensive patterns
Strategy: validation
Validate before calling
fn interval_directions_ok(css: &str) -> bool {
for cap in css.split('(').skip(1) {
let inner = cap.split(')').next().unwrap_or("");
let toks: Vec<&str> = inner.split_whitespace().collect();
if toks.len() == 5 {
let lt_family = |o: &str| o == "<" || o == "<=";
let (a, b) = (toks[1], toks[3]);
if ["<", "<=", ">", ">="].contains(&a) && ["<", "<=", ">", ">="].contains(&b) {
if lt_family(a) != lt_family(b) {
return false;
}
}
}
}
true
} Type guard
fn is_interval_direction_error(e: &swc_css_parser::error::Error) -> bool {
matches!(e.kind(), swc_css_parser::error::ErrorKind::Expected(m) if *m == "left comparison operator should be equal right comparison operator")
} Try / catch
match parse_file::<Stylesheet>(&fm, None, config, &mut errors) {
Ok(sheet) => handle(sheet),
Err(err) if is_interval_direction_error(&err) => {
let (span, _) = *err.into_inner();
hint_at_span(css, span, "both operators in a range interval must point the same way, e.g. (400px <= width <= 700px)");
}
Err(err) => return Err(err.into()),
} Prevention
- Derive the second operator from the first in codegen instead of using two variables
- Prefer two conjoined plain features when generating bounds
- Add a unit test asserting emitted intervals never mix directions
- Review min/max template substitution order
When it happens
Trigger: '@media (400px <= width >= 700px)', '@media (400px < width >= 700px)' — any interval whose two operators have opposite directions.
Common situations: Hand-typed intervals where the second operator is flipped, and code generation that interpolates 'min' and 'max' operators independently so they can disagree.
Related errors
- Expected '>' or '<' operators
- Expected identifier value
- Expected number, ident, dimension or function token
- Expected ident (exclude the keywords 'only', 'not', 'and', '
- Expected function or '('
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/c061026a3ae5bbaa.
Report an issue: GitHub.