DEV Community

Count Of Tuscany
Count Of Tuscany

Posted on

RE2 vs PCRE: Every Regex Construct That Silently Breaks in GA4 (and the Fix)

If your regex works in JavaScript but returns zero rows in GA4, RE2 is almost certainly the reason. GA4 (and BigQuery, Go, Rust) runs Google's RE2 engine, which is linear-time but rejects several PCRE families entirely.

Here's the complete list and the fix for each.

What RE2 rejects

PCRE construct Example RE2 verdict Replacement
Lookahead foo(?=bar) rejected Capture the suffix: (foo)bar
Negative lookahead (?!admin) rejected Negated class or exclude filter
Lookbehind (?<=/)(\w+) rejected Capture group: /(\w+)
Backreference `(foo\ bar)\1` rejected
Atomic group (?>a+)b rejected Plain group (a+)b
Possessive quantifier a++ rejected Greedy a+ (RE2 is linear anyway)
Recursion (?R) rejected Unroll the recursion
Named groups (some) (?P<name>...) partial Use numbered groups (...)
\w with Unicode \w+ (Unicode) ASCII only [A-Za-z0-9_] or use Unicode classes where supported

The three that bite most

1. Lookahead in GA4 segments

You want "page paths containing /blog/ but not /blog/admin/". In PCRE: .*/blog/(?!admin).*. In RE2 this is rejected silently — GA4 returns zero rows, no error.

Fix: Use GA4's exclude filter, or restructure: .*/blog/[^a].* (rough, but RE2-safe). For exact exclusions, use two filters: include .*/blog/.* AND exclude .*/blog/admin/.*.

2. Backreferences in event names

You want repeated words: (\w+)\s+\1. RE2 rejects this.

Fix: Expand: (\w+)\s+\w+ won't match the same word. If you need the same word twice, list them: hello hello|world world.

3. Non-greedy .*?

.*? works in RE2 (it's supported), but in GA4's full-match semantics it's usually pointless — GA4 matches the entire value anyway, so greedy .* is what you want.

The RE2 mindset

  1. Full match by default. GA4 matches the entire value. Wrap substring patterns in .* on both sides.
  2. No lookahead/lookbehind. Restructure with capture groups or use exclude filters.
  3. No backreferences. Expand or use multiple patterns.
  4. Test in RE2, not JS. JavaScript RegExp accepts constructs RE2 rejects. Always verify in an RE2 engine (Go's regexp, Rust's regex, or a tester that flags RE2 compatibility).

Free tooling

I built RegexLab — a free, browser-only regex tester that:

  • Shows match/no-match per line
  • Flags RE2-incompatible constructs
  • Has a pattern library with 52 tested patterns (dates, URLs, UUIDs, IPs, UTM parameters, log levels) — each with an RE2-compatibility note
  • Has a dedicated RE2 vs PCRE page with the full construct list and replacements

Nothing leaves your browser. The whole thing runs on client-side JavaScript RegExp.

TL;DR

  • GA4 = RE2 engine. No lookahead, no backreferences, no atomic groups.
  • Restructure with capture groups, exclude filters, or expanded alternation.
  • Test in RE2 before shipping. JavaScript-valid ≠ GA4-valid.

What's the RE2 incompatibility that cost you the most debugging time? Mine was a lookahead that matched everything in preview and nothing in production.

Top comments (0)