oxc-project/oxc · warning · OxcDiagnostic

Unexpected surrogate pair in character class.

Error message

Unexpected surrogate pair in character class.

What it means

Diagnostic from oxlint's eslint/no-misleading-character-class rule (crates/oxc_linter/src/rules/eslint/no_misleading_character_class.rs:30). It reports a surrogate pair written as two \uD800-\uDFFF escapes inside a character class of a unicode-mode regex (/u or /v): in unicode mode each escape is a code unit, so /[\uD83D\uDC4D]/u matches the two lone surrogates, not the emoji. The help tells you to write the code point directly with \u{...}.

Source

Thrown at crates/oxc_linter/src/rules/eslint/no_misleading_character_class.rs:30

    visit::{RegExpAstKind, Visit},
};
use oxc_span::{GetSpan, Span};
use schemars::JsonSchema;
use serde::Deserialize;

use crate::{
    AstNode,
    context::LintContext,
    fixer::{RuleFix, RuleFixer},
    rule::{DefaultRuleConfig, Rule},
    utils::{
        RegexFlagsParseResult, get_regex_flags_span, get_regex_pattern_span, is_regexp_callee,
        run_on_regex_node,
    },
};

fn surrogate_pair_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unexpected surrogate pair in character class.")
        .with_help("Use Unicode code point escapes (e.g., \\u{1F44D}) instead of surrogate pairs.")
        .with_label(span)
}

fn surrogate_pair_without_flag_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unexpected surrogate pair in character class.")
        .with_help("Add the Unicode flag 'u'.")
        .with_label(span)
}

fn combining_class_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unexpected combining class in character class.")
        .with_help("Replace the character with its normalized form (NFC) or use Unicode code point escapes instead of combining sequences.")
        .with_label(span)
}

fn emoji_modifiers_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unexpected emoji modifier in character class.")

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Replace each surrogate pair with a code point escape: /[\u{1F44D}]/u
  2. Or match the literal character directly: /👍/u
  3. For dynamic patterns, build from code points (String.fromCodePoint) rather than concatenated surrogate escapes

Example fix

// before
const emoji = /[\uD83D\uDC4D]/u; // matches two lone surrogates, not 👍

// after
const emoji = /[\u{1F44D}]/u; // code point escape for 👍
Defensive patterns

Strategy: validation

Validate before calling

// Reject surrogate escapes in unicode-mode patterns before they ship
function assertNoSurrogateEscapes(pattern, flags) {
  if ((flags.includes('u') || flags.includes('v')) && /\\uD[89A-F][\\dA-F]{2}/i.test(pattern)) {
    throw new Error('surrogate escapes in a /u regex character class; use \\u{...} code point escapes');
  }
}

Type guard

const usesCodePointEscapes = (pattern) => /\\u\{[0-9a-f]+\}/i.test(pattern);

Prevention

When it happens

Trigger: /[\uD83D\uDC4D]/u, new RegExp('[\\u{1F44D}]|\\uD83D\\uDC4D', 'u'), or character classes in v-mode regexes containing paired surrogate escapes; both literal and dynamically built patterns reaching the RegExp constructor are checked.

Common situations: Porting pre-ES2015 emoji regexes to unicode mode by just adding the u flag; escaping non-BMP characters with tools that emit UTF-16 code-unit escapes; filtering emoji from usernames/chat while missing astral characters.

Related errors


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