Replacement References A Group The Pattern Does Not Have

ID

javascript.regex_replacement_group_reference

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

JavaScript

Tags

regex, reliability

Description

Reports a replace or replaceAll whose replacement string references a capture group the pattern does not have. JavaScript does not fail here: an out-of-range $n or an undeclared $<name> is substituted with the empty string, so the call quietly produces text with a piece missing.

The rule requires the pattern to declare at least one capturing group before judging a numeric reference. In a group-less pattern the language leaves $1 as literal text, which is exactly what a currency template wants, so only once a group is written is $n unambiguously meant as a reference. A named reference is judged on the same principle.

The replacement must be a single string literal; a template literal, a concatenation or a variable is built at run time and carries no decidable reference. The replacer-function form takes its groups as parameters and is not a replacement string. $$, $&, the backtick form and $' are the language’s own escapes and never group references.

Rationale

// Bad
"2026-09".replace(/(\d+)-(\d+)/, "$3/$1");        // "/2026" -- $3 vanished
"2026".replace(/(?<y>\d{4})/, "$<yer>");          // "" -- the group is named y
  • Nothing throws and nothing warns. The result is a string that is merely wrong, usually shorter than expected.

  • The defect survives review because the intent reads correctly: the reader counts the groups in the pattern and the references in the replacement separately, and they are far apart on the line.

  • A mistyped group name behaves the same way as a mistyped one-off, so a rename in the pattern silently breaks every replacement that referred to the old name.

Remediation

Renumber the references to the groups the pattern actually captures, or add the group the replacement expects. Named groups make the pairing checkable by eye.

// Good
"2026-09".replace(/(\d+)-(\d+)/, "$2/$1");
"2026".replace(/(?<y>\d{4})/, "$<y>");

// Also good: a non-capturing group does not consume a number
"ab".replace(/(?:a)(b)/, "$1");

© 2026 Xygeni. All rights reserved