Fix GA4 Unassigned Traffic with Source/Medium Regex
An Unassigned row in your channel report means one thing: sessions arrived whose source/medium values matched none of the 19 default channel rules. You cannot edit the default rules — but you can write a custom channel group whose regex conditions capture exactly those values. This page walks the fix end to end: find the culprit values, test the pattern, paste it in.
TL;DR: Traffic acquisition report → add secondary dimension Session source / medium → filter to Unassigned → read the offending values → write one regex per group in a custom channel group. Channel groups reclassify history too; UTM fixes and report filters do not.
Why GA4 shows Unassigned in the first place
GA4 classifies every session into a channel group using a fixed set of 19 default rules (Organic Search, Paid Search, Email, Social, Referral, and so on). Each rule matches specific source/medium values — google / cpc is Paid Search, newsletter / email is Email, and so on. When a session's values match none of the rules, GA4 puts it in Unassigned instead of guessing.
The usual suspects that fall through the rules:
- Email platforms sending with their own domain as source and
(not set)as medium (Substack, beehiiv, Ghost — anything that isn't explicitly tagged) - Custom mediums your team invented:
fb_ads,ig_ads,pmax,newsletter,crm - Mixed-case values like
Emailfrom older tags — actually fine, see the case-insensitivity note below - AI assistants sending referrals:
chatgpt.com,claude.ai,perplexity.ai— no default rule existed for these until recently, and most properties still have none - Partner, affiliate and internal invite links with nonstandard sources
Step 1 — find the exact values causing Unassigned
Before writing any regex, read the data. In GA4:
- Open Reports → Acquisition → Traffic acquisition
- Set the date range large enough to matter (90 days gives the pattern statistically useful weight)
- Add secondary dimension Session source / medium
- Filter the table to Session default channel group | exactly matches | Unassigned
- Sort by sessions descending
You now have the literal list of offending source / medium pairs with their volumes. Group them mentally: all the email platforms together, all the ad mediums together, the AI referrers together. One regex per group — not one giant pattern trying to catch everything, which nobody can debug six months later.
Step 2 — write the pattern against real values
Two GA4 rules govern every pattern you will write here — both are covered in depth on the GA4 regex match types page:
- Full-match semantics.
facebookmatches only the exact valuefacebook, notfacebook.com. For contains-behavior, wrap:.*facebook.* - RE2 engine. No lookahead, no backreferences. The patterns below are all RE2-safe — and channel-grouping regex is also case-insensitive, so
emailmatchesEmailandEMAILwithout any flag
Each pattern below has a Try link that loads it into the tester with a realistic sample value — paste your own offending values from step 1 and confirm the match before touching GA4.
6 ready-made Unassigned fix patterns
These go into a custom channel group (Reports → Library → edit the channel group, or Admin → Channel groups). Each row: the condition you set, the regex, and a sample value it recaptures.
| Offender | Channel-group condition | Regex | |
|---|---|---|---|
| Newsletter platforms, untagged | Session source matches | (substack|beehiiv|ghost|mailchimp|convertkit|klaviyo|list-manage|customer\.io|sendgrid) |
Try → |
| Capitalized "Email" medium | Session medium matches | email|e-mail|em |
Try → |
| Meta ads with custom medium | Session medium matches | ^(fb_ads|ig_ads|meta_ads|facebook_ads|instagram_ads)$ |
Try → |
| AI assistants & answer engines | Session source matches | (chatgpt|chat\.openai|claude|perplexity|gemini|copilot|edgeservices\.bing|duck\.com|brave\.search) |
Try → |
| Partner & affiliate links | Session source matches | .*(partner|affiliate|aff)[-_.].*|^(partner|affiliate|aff)$ |
Try → |
| Invite / tool links (Calendly, forms) | Session source matches | (calendly|typeform|forms\.gle|tally|loom|notion\.so) |
Try → |
Build the AI-referrer channel first if you have not touched channel groups in 2025–2026 — assistant-referred traffic is the fastest-growing unassigned category, and "AI Referrals" as its own channel turns a mystery row into a reportable segment. The full custom channel grouping regex guide covers rule ordering and the default 19 rules these patterns slot alongside.
Step 3 — where to apply it (and what reprocesses history)
| Surface | What it does | History re-sorts? |
|---|---|---|
| Custom channel group (Admin → Channel groups) | Rewrites the channel dimension itself for reports using the group | Yes — retroactive |
| Report filter (table filter, exploration filter) | Hides rows in that one view | No — view only |
| Segment (explorations) | Selects sessions for that analysis | No — classification unchanged |
| Fixed UTMs (going forward) | New sessions arrive with clean values | No — old hits keep old values |
This table is the whole reason Unassigned "comes back" every quarter for most teams: someone fixes the tags, sees Unassigned unchanged, and concludes the fix failed. The tags only govern the future. The channel group is the retroactive fix — and because it is applied at query time, editing its regex next month re-sorts history again with the new rules.
Step 4 — fix the tags too (the forward-looking half)
Channel groups bandage the reporting layer; the underlying tagging should still be corrected, or the gap keeps growing. For every group you captured above, work backwards to the tag:
- Newsletter platforms: append
utm_source=<platform>&utm_medium=emailto every outgoing link (most platforms have a default-parameters setting — set it once) - Meta ads: let the Ads Manager auto-UTM do
utm_medium=paid(which default Paid Social catches) rather than a customfb_adsonly your channel group understands - Partner links: a shared link-builder with locked
utm_source=partnerbeats polite requests in Slack
Then verify each new tag lands correctly — the tester with the UTM parameter regex patterns makes a fast QA sheet for link exports.
FAQ
Does fixing Unassigned traffic reprocess my historical data?
Custom channel groups: yes — rules apply when reports are computed, so past sessions re-sort the moment you save. Raw source/medium values: no — immutable; corrected UTMs only affect future sessions. Report filters change nothing, they only hide rows in that view.
Why does my regex not match in a channel grouping rule?
Full-match semantics again: facebook won't match facebook.com — wrap it as .*facebook.*. Also confirm you edited a custom channel group: the 19 default rules are fixed and cannot be changed. When in doubt, paste the exact value from the secondary dimension into the tester — if it doesn't match there, it will never match in GA4 either.
Is GA4 channel grouping regex case-sensitive?
No — channel-group regex conditions are case-insensitive, so email also matches Email and EMAIL. (Other GA4 regex surfaces vary — the match types guide lists where each behavior applies.)
What's the difference between a custom channel group and a segment?
A channel group rewrites the Session default channel group dimension itself, retroactively, everywhere the group is used. A segment only filters which rows one report or exploration shows — the sessions stay classified Unassigned everywhere else. To actually fix the number, use a channel group.
Related on RegexLab: the custom channel grouping regex guide (all default rules + pattern templates), the GA4 match types guide (full vs partial match per surface), the GA4 regex cheat sheet, the UTM parameter regex and URL regex pattern pages, and RE2 vs PCRE.