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
- Replace each surrogate pair with a code point escape: /[\u{1F44D}]/u
- Or match the literal character directly: /👍/u
- 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
- Always write astral characters in unicode-mode regexes as \u{...} or literal characters
- Add unit tests matching astral inputs (emoji, CJK ext-B) to every unicode regex
- Run no-misleading-character-class in CI to catch surrogate escapes introduced by refactors
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
- Invalid regular expression: `u` and `v` flags should be used
- Unexpected combining class in character class.
- A regular expression literal can be confused with '/='.
- Empty character class will not match anything
- Invalid regular expression: Duplicated flag
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/2f657403025676dd.
Report an issue: GitHub.