oxc-project/oxc · warning

Unexpected control character

Error message

Unexpected control character

What it means

Diagnostic from the oxlint rule `no-control-regex`, singular branch (crates/oxc_linter/src/rules/eslint/no_control_regex.rs:33). When a regular expression contains EXACTLY ONE control character (ASCII 0x00–0x1F plus 0x7F), the message is 'Unexpected control character', with a label rendering that character as `\xNN`, `\uNNNN`, or `U+XXXX` if unprintable. Invisible control characters in patterns are a classic source of regexes that match nothing the author can see.

Source

Thrown at crates/oxc_linter/src/rules/eslint/no_control_regex.rs:33

        .iter()
        .map(|ch| {
            let label = match ch.kind {
                CharacterKind::Octal1 | CharacterKind::Octal2 | CharacterKind::Octal3 => {
                    format!(
                        "'{ch}' is a control character. It looks like a backreference, but there is no corresponding capture group."
                    )
                }
                _ => {
                    // Show the code point since the character itself is not printable
                    let ch = format!("U+{:04X}", ch.value);
                    format!("'{ch}' is a control character.")
                }
            };
            ch.span.label(label)
        })
        .collect();

    OxcDiagnostic::warn(if count > 1 {
        "Unexpected control characters"
    } else {
        "Unexpected control character"
    })
    .with_help(
        "Avoid matching control characters in regular expressions. If intentional, disable this rule for the expression.",
    )
    .with_labels(labels)
}
#[derive(Debug, Default, Clone)]
pub struct NoControlRegex;

declare_oxc_lint!(
    /// ### What it does
    ///
    /// Disallows control characters and some escape sequences that match
    /// control characters in regular expressions.
    ///

View on GitHub (pinned to 36ec0ef2ba)

Solutions

  1. Delete the stray control character if it is a paste artifact.
  2. If the match is intentional, replace it with an explicit, visible escape and suppress the rule on that line (`// oxlint-disable-next-line no-control-regex`).
  3. Anchor on the surrounding printable pattern instead of the control char when possible (e.g. match the delimiter token you can see).
  4. Sanitize inputs earlier (`str.replace(/[\x00-\x1F\x7F]/g, '')`) so regexes never need to match control characters.

Example fix

// before
const end = /\x03/; // single control character (ETX)

// after
const end = /\u0003/; // explicit, plus inline suppression if required
// oxlint-disable-next-line no-control-regex
Defensive patterns

Strategy: validation

Validate before calling

// Gate: any control character (raw or escaped) inside a regex literal
const hasControlInRegex = /\/(?:[^\/\n\\]|\\.)*\\x[01][0-9A-Fa-f](?:[^\/\n\\]|\\.)*\//.test(src) ||
  /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/.test(src);

Prevention

When it happens

Trigger: A regex literal or RegExp string containing exactly one control character — e.g. `/\x1F/` (unit separator), a raw pasted 0x7F DEL byte inside a character class, or `/\x00/` NUL matching. `count == 1` selects the singular message.

Common situations: Single escape left behind after cleaning a pasted pattern; matching a vendor log format that terminates lines with 0x03; CSV/EDI parsing with stray control separators; linting generated fixtures that embed one control byte.

Related errors


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