zloirock/core-js · error · SyntaxError
Invalid capture group name
Error message
Invalid capture group name
What it means
The RegExp constructor polyfill supports named capture groups. When parsing a pattern containing a named group (?<name>...), it validates that the name is non-empty and not already used. A duplicate or empty name raises a SyntaxError at construction time, matching ES2018 spec validation performed natively by modern engines.
Source
Thrown at packages/core-js/modules/es.regexp.constructor.js:115
} else if (chr === ']') {
brackets = false;
} else if (!brackets) switch (true) {
case chr === '[':
brackets = true;
break;
case chr === '(':
result += chr;
if (exec(IS_NCG, stringSlice(string, index + 1))) {
index += 2;
ncg = true;
groupid++;
} else if (charAt(string, index + 1) !== '?') {
groupid++;
}
continue;
case chr === '>' && ncg:
if (groupname === '' || hasOwn(names, groupname)) {
throw new SyntaxError('Invalid capture group name');
}
names[groupname] = true;
named[named.length] = [groupname, groupid];
ncg = false;
groupname = '';
continue;
}
if (ncg) groupname += chr;
else result += chr;
}
// convert `\k<name>` backreferences to numbered backreferences
for (var ni = 0; ni < named.length; ni++) {
var backref = '\\k<' + named[ni][0] + '>';
var numRef = '\\' + named[ni][1];
while (stringIndexOf(result, backref) > -1) {
result = replace(result, backref, numRef);
}
} return [result, named];View on GitHub (pinned to 84e45fba09)
Solutions
- Rename one of the duplicate capture groups so every (?<name>) in the pattern is unique.
- Check for empty or malformed names: use only IdentifierPart chars and ensure a name follows '?<'.
- If merging patterns, wrap fragments in non-capturing groups and rename colliding groups programmatically.
- Test on the target environment — on modern engines the native RegExp throws the same class of SyntaxError earlier, so fix the pattern at the source.
Example fix
// before
const re = new RegExp('(?<year>\\d{4})-(?<year>\\d{2})'); // SyntaxError
// after
const re = new RegExp('(?<year>\\d{4})-(?<month>\\d{2})'); Defensive patterns
Strategy: validation
Validate before calling
function validateGroupNames(pattern) {
const names = [...pattern.matchAll(/\(\?<([^>]+)>/g)].map(m => m[1]);
const dup = names.filter((n, i) => names.indexOf(n) !== i);
if (dup.length || names.some(n => !n)) throw new SyntaxError('Invalid capture group name in: ' + pattern);
}
validateGroupNames(pattern); const re = new RegExp(pattern); Type guard
function hasUniqueNamedGroups(pattern) {
const names = [...pattern.matchAll(/\(\?<([^>]+)>/g)].map(m => m[1]);
return new Set(names).size === names.length && !names.includes('');
}
// usage: if (hasUniqueNamedGroups(pattern)) new RegExp(pattern); Try / catch
let re;
try {
re = new RegExp(pattern);
} catch (e) {
if (e instanceof SyntaxError && /capture group/.test(e.message)) {
// fall back to unnamed groups or log pattern for manual fix
} else throw e;
} Prevention
- Keep named groups unique per pattern, including across alternations.
- When concatenating pattern fragments, namespace group names or use non-capturing groups.
- Test regex construction on the oldest supported browser where core-js polyfill is active.
- Extract group names with matchAll to validate before building RegExp.
When it happens
Trigger: new RegExp('(?<a>x)(?<a>y)', flags) — duplicate group name; pattern like '(?<x>...)' where the polyfill's manual scan ends with an empty groupname; duplicate names across alternations '(?<year>\d{4})|(?<year>\d{2})'; running the same pattern twice after concatenating fragments that each declare the same name.
Common situations: Building regexes dynamically by concatenating pattern fragments that share group names; porting regexes from other languages (PCRE allows duplicate names with different backrefs); polyfilled environments (older browsers/Node) where native RegExp would have rejected earlier with a slightly different message; copy-pasting two regexes and merging their named groups.
Related errors
- Cannot convert a Symbol value to a number
- Promise can't be resolved itself
- Symbol is not a constructor
- <symbol> is not a symbol (dynamic: tryToString(sym) + ' is n
- ArrayBuffer expected
AI-assisted analysis of zloirock/core-js@84e45fba09 (2026-08-30).
Data as JSON: /api/errors/71954df826628fd0.
Report an issue: GitHub.