oxc-project/oxc · warning

Unexpected constant relational comparison

Error message

Unexpected constant relational comparison

What it means

Diagnostic from `no-constant-binary-expression`, emitted by `constant_relational_comparison` (crates/oxc_linter/src/rules/eslint/no_constant_binary_expression.rs:109). It fires on relational operators (`<`, `<=`, `>`, `>=`) where BOTH sides are static literals (including `-`/`+`/`~`-prefixed literals, template literals without substitutions, and global `undefined`) — e.g. `5 < 10`, `'a' < 'b'`. The compiler can fold the result, so a constant relational comparison in source is almost certainly a placeholder or mistake. It is controlled by the `checkRelationalComparisons` config, which defaults to true.

Source

Thrown at crates/oxc_linter/src/rules/eslint/no_constant_binary_expression.rs:110

            "This compares constantly with the {left_or_right}-hand side of the {operator}"
        ))
        .with_label(span)
}

fn constant_always_new(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unexpected comparison to newly constructed object")
        .with_help("These two values can never be equal")
        .with_label(span)
}

fn constant_both_always_new(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unexpected comparison of two newly constructed objects")
        .with_help("These two values can never be equal")
        .with_label(span)
}

fn constant_relational_comparison(operator: &str, span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unexpected constant relational comparison")
        .with_help(format!("Both sides of the {operator} are literal values"))
        .with_label(span)
}

impl Rule for NoConstantBinaryExpression {
    fn from_configuration(value: serde_json::Value) -> Result<Self, serde_json::error::Error> {
        DefaultRuleConfig::<Self>::from_value(value).map(DefaultRuleConfig::into_inner)
    }

    fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
        match node.kind() {
            AstKind::LogicalExpression(expr) => match expr.operator {
                LogicalOperator::Or | LogicalOperator::And if expr.left.is_constant(true, ctx) => {
                    ctx.diagnostic(constant_short_circuit(
                        "truthiness",
                        expr.operator.as_str(),
                        expr.span,
                    ));

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Replace the constant comparison with its folded boolean result (`5 < 10` → `true`) or remove it.
  2. If one side was meant to be a variable, restore the variable instead of the literal.
  3. Keep constant thresholds in named bindings and compare variable-to-binding: `if (retryCount < MAX_RETRIES)`.
  4. For generated files where literal comparisons are expected, set the rule option `{ "checkRelationalComparisons": false }` in .oxlintrc.json or exclude the directory.

Example fix

// before
if (5 < 10) {
  doSomething();
}

// after
if (retryCount < MAX_RETRIES) {
  doSomething();
}
Defensive patterns

Strategy: validation

Validate before calling

// Gate: literal-on-both-sides relational comparisons
const literalRelational = /\b(-?\d+(\.\d+)?n?|['"`][^'"`]*['"`]|true|false|null|undefined)\s*[<>]=?\s*(-?\d+(\.\d+)?n?|['"`][^'"`]*['"`]|true|false|null|undefined)/.test(src);

Prevention

When it happens

Trigger: BinaryExpression whose `operator.is_compare()` and both `left` and `right` pass `is_static_literal` — e.g. `10 >= 5`, `true >= false`, `` `foo` < `bar` ``, `null >= 5`, `-5 < 10` — with `checkRelationalComparisons: true` (the default).

Common situations: Minified/generated code that inlines constant comparisons; threshold constants pasted on both sides of the operator while editing; test fixtures that lint along with source; enabling the eslint `correctness` preset over previously unlinted generated files.

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/3f7890f599f13659. Report an issue: GitHub.