flashman
← All posts

JavaScript regex v flag set debugging

Debug a character class that throws under the JavaScript v flag by escaping set-syntax characters, then compare one global matchAll pass with the u flag.

2026-10-17 · 6 min read · Rahul Chitturi

  • regex
  • unicode
  • javascript

The JavaScript v flag turns a character class into a set expression. -- is set difference. && is intersection. A nested class is a set, not a literal bracket. The u flag still gives you Unicode mode and property escapes, and it does not give you those operators. u and v cannot be set together. new RegExp('a', 'uv') throws because the flags are invalid.

Property escapes are not active without one of those flags. A pattern of \p{L} with only g compiles in the tester and does not match the letters in Ab. Choose u or v on purpose, and include g when you need a list of matches.

Escape the operator when you wanted a literal

On the text AbC9, [\p{L}--\p{Lu}] with gv matches b. Letters minus uppercase leaves the lowercase letter. [\p{L}&&\p{Lu}] with gv matches A and C. [[a-c]--[b]] with gv matches a and c. The same nested class with gu throws, because u mode does not treat the brackets as a nested set.

[a--b] with gv matches a from the text a-b. The class is the set a minus the set b, so the hyphen is the operator and is not a member. [a\-\-b] with gv matches a, the hyphen, and b. An unescaped ( inside a v-mode class throws Invalid character in character class. [\(] matches the parenthesis. [a--b] with gu throws Range out of order, so the same source is not a portable way to write two hyphens.

  • Use gv for a set expression you want to list.
  • Escape ( ) [ ] { } / - \ | when they are literals.
  • Write -- only when you mean set difference.
  • If the engine rejects flag v, stay on u and drop the set operators.

Compare one fresh pass

Flashman's regex tester compiles the pattern and runs matchAll once. matchAll requires a global expression, so flag v alone throws. The result lists the index and any positional captures. It builds a new expression every time, so it will not show a lastIndex carried from a previous run. Sticky matching is a separate flag.

Paste the fixture twice, once with gu and once with gv, and diff the match lists. A throw on one side is the finding. Use the JSON formatter when a service logs the match indexes. Keep the pattern, the flags, and the fixture together, because the flags change what the brackets mean.

Try these tools