Skip to main content
UTMBy SendURL team13 min read

GA4 Unassigned Traffic From UTMs: Causes and Fixes

Part of the UTM handbook

When GA4 files UTM-tagged traffic as Unassigned, one of your values matched none of its channel rules. How to find which, fix the links, and regroup the data you already have.

Unassigned in GA4 means a session matched none of the default channel rules. Google defines it as "the value Analytics uses when there are no other channel rules that match the event data." For UTM-tagged traffic, the cause is almost always a utm_medium or utm_source value that GA4's rules don't recognise, such as utm_medium=newsletter or utm_source=insta&utm_medium=story.

The fix is to change the values on your links, then regroup the old data with a custom channel group. This guide shows how to find the bad values, what each one misses, and how to test a tagged link before it goes out. It's part of the UTM parameters handbook.

What Unassigned means

GA4 sorts every session into one channel of its default channel group using fixed rules on source, medium and campaign name. The rules can't be changed: "Channel definitions aren't case sensitive and can't be edited." When no rule matches, the session's channel is Unassigned.

Nothing is lost. The session, its source, its medium and its campaign are all recorded. Only the channel label is missing, so channel-level reports undercount Email, Organic Social and the rest by however much is sitting in Unassigned.

Unassigned is different from Direct. Direct means GA4 found no source for the visit ((direct) / (none)). If your tagged links show up as Direct, the UTMs most likely never reached the page; skip to cause 5.

Find the Unassigned rows

  1. Open Reports → Acquisition → Traffic acquisition. Its default dimension is the session's default channel group.
  2. Click + next to the dimension drop-down and add Session source / medium.
  3. Type Unassigned in the table's search box.

You now see the exact source/medium pairs GA4 couldn't place. If they're your UTM values (newsletter / newsletter, insta / story), it's a naming problem and the rest of this guide fixes it. If both values read (not set), the session has no traffic-source data at all, which points at the tag setup rather than your links. If only the medium does (newsletter / (not set)), the link is missing utm_medium.

For a longer view, an exploration does the same with campaigns:

Explore → Free formText
Dimensions:  Session default channel group, Session source / medium, Session campaign
Metrics:     Sessions
Rows:        Session source / medium, Session campaign
Filter:      Session default channel group exactly matches Unassigned
Date range:  last 90 days

The 7 common causes

#Symptom in the reportCauseFix
1newsletter / newsletter, partner / podcast, poster / qrThe medium matches no ruleUse a medium from Google's rules: email, audio. For QR and print, add a custom channel
2insta / story, x / organic-socialSource isn't on Google's social list and the medium isn't one of its social valuesutm_medium=social (works with any source)
3newsletter / email-newsletter, adnet / cost-per-clickA near miss: Email needs an exact value, the paid pattern needs cp togetheremail, cpc
4ig_bio / InstagramBoth values made up on the spotinstagram / social with utm_content=bio-link-2
5Tagged clicks show as (direct) / (none), not UnassignedThe UTMs never reached the pageCheck the redirect chain (below)
6newsletter / (not set)A required parameter is missing. Google: "Missing UTM parameters will result in (not set) values"Add utm_source, utm_medium and utm_campaign to every link
7(not set) / (not set) under UnassignedNo UTM values at all: the session arrived without traffic-source dataCheck the GA4 tag setup in DebugView

With a source on Google's search list, such as bing / cost-per-click, the session doesn't even reach Unassigned: it lands in Organic Search, so paid clicks are counted as organic. Check Organic Search for your ad sources too.

Causes 1 to 4 come straight from the rules. Social sources are matched against Google's list of sources and categories (a PDF dated 9 February 2023). instagram, ig, facebook, tiktok, linkedin and whatsapp are on it. insta, x, threads and bsky aren't, so those only reach Organic Social with utm_medium=social. For LinkedIn posts, first comments and colleagues' shares, the LinkedIn link tracking guide uses linkedin with social, and paid-social for ads.

Cause 5 has a few usual suspects:

  • The tags were added to the short link, not its destination. snu.to/autumn-ig-bio2?utm_source=instagram doesn't pass the query on with SendURL (and HTTP redirects don't carry the query over by themselves: RFC 9110 only says a redirect inherits the fragment). Put the UTMs in the destination URL. How to shorten a UTM link walks through it.
  • A redirect on your own site dropped the query. An old page that forwards to a new one, a forced www or https hop, or a language redirect can lose everything after ?.
  • The UTMs sit after a #. In example.com/sale#top?utm_source=newsletter the tags are part of the fragment, not the query string.

Worked examples

Each row is read from Google's default channel rules as published on 15 October 2026:

utm_sourceutm_mediumGA4 channelWhy
instagramsocialOrganic SocialSource on the social list, medium social
instagramInstagramOrganic SocialSource on the list; the medium doesn't matter
instastoryUnassignedSource not on the list, story matches no rule
instasocialOrganic SocialThe medium rescues it
ig_bioInstagramUnassignedNeither value matches anything
instagrampaid-socialPaid SocialSocial source + paid medium
instapaid-socialPaid OtherPaid medium, but the source isn't on the list
facebooksocial-paidOrganic Socialpaid.* only matches at the start, so not paid
newsletterEmailEmailChannel rules ignore case (the report still shows Email as its own row)
newsletternewsletterUnassignedNeither value is email
posterqrUnassignedNo rule for print
xorganic-socialUnassignedorganic-social isn't one of Google's social mediums
whatsappsmsOrganic Social or SMSBoth rules match and Google doesn't document which wins. Use social

Check a pair before you use it

This Python function applies Google's rules for manually tagged traffic and returns every channel a pair matches, in the order Google lists them. The source lists are trimmed to common networks; for anything else, look the source up in Google's PDF.

ga4_channel.pyPython
import re

# Trimmed from Google's source categories list (PDF dated 2023-02-09).
SOCIAL = {"facebook", "fb", "m.facebook.com", "l.facebook.com", "instagram", "ig", "l.instagram.com",
          "linkedin", "lnkd.in", "tiktok", "twitter", "t.co", "pinterest", "reddit", "whatsapp",
          "snapchat", "quora", "tumblr", "t.me", "messenger"}
SEARCH = {"google", "bing", "duckduckgo", "yahoo", "baidu", "yandex", "naver", "qwant"}
VIDEO = {"youtube", "youtube.com", "m.youtube.com", "vimeo", "twitch"}
SHOPPING = {"amazon", "amazon.com", "alibaba", "walmart", "walmart.com", "mercadolibre", "shop.app"}

PAID = re.compile(r"^(.*cp.*|ppc|retargeting|paid.*)$")
SHOP_CAMPAIGN = re.compile(r"^(.*(([^a-df-z]|^)shop|shopping).*)$")
EMAIL = {"email", "e-mail", "e_mail", "e mail"}
SOCIAL_MEDIUMS = {"social", "social-network", "social-media", "sm", "social network", "social media"}


def ga4_channels(source, medium, campaign=""):
    s, m, c = source.lower(), medium.lower(), campaign.lower()
    paid, shop = bool(PAID.match(m)), s in SHOPPING or bool(SHOP_CAMPAIGN.match(c))
    rules = [
        ("Direct", s == "(direct)" and m in ("(not set)", "(none)")),
        ("Cross-network", "cross-network" in c),
        ("Paid Shopping", shop and paid),
        ("Paid Search", s in SEARCH and paid),
        ("Paid Social", s in SOCIAL and paid),
        ("Paid Video", s in VIDEO and paid),
        ("Display", m in ("display", "banner", "expandable", "interstitial", "cpm")),
        ("Paid Other", paid),
        ("Organic Shopping", shop),
        ("Organic Social", s in SOCIAL or m in SOCIAL_MEDIUMS),
        ("Organic Video", s in VIDEO or "video" in m),
        ("Organic Search", s in SEARCH or m == "organic"),
        ("AI Assistant", m == "ai-assistant"),
        ("Referral", m in ("referral", "app", "link")),
        ("Email", s in EMAIL or m in EMAIL),
        ("Affiliates", m == "affiliate"),
        ("Audio", m == "audio"),
        ("SMS", s == "sms" or m == "sms"),
        ("Mobile Push Notifications",
         m.endswith("push") or "mobile" in m or "notification" in m or s == "firebase"),
    ]
    return [name for name, hit in rules if hit] or ["Unassigned"]


for pair in [("insta", "story"), ("instagram", "social"), ("newsletter", "newsletter"),
             ("facebook", "paid-social"), ("whatsapp", "sms")]:
    print(pair, ga4_channels(*pair))
# ('insta', 'story') ['Unassigned']
# ('instagram', 'social') ['Organic Social']
# ('newsletter', 'newsletter') ['Unassigned']
# ('facebook', 'paid-social') ['Paid Social', 'Paid Other', 'Organic Social']
# ('whatsapp', 'sms') ['Organic Social', 'SMS']

More than one result is normal for paid traffic: the specific paid channel (Paid Social here) is listed first and is the one you'll see. For unpaid traffic, more than one result means a pair Google doesn't settle, like whatsapp + sms. Change one of the values.

Three checks, in order. Each takes a minute.

1. Lint the values. Run them through the function above, or the lint script in the UTM naming convention guide.

2. Follow the redirects and read the final URL. curl -sIL prints every hop's location header, so you can see whether the UTMs survive to the last one:

Every hop, from the short link to the final pageShell
curl -sIL https://snu.to/autumn-ig-bio2 | grep -iE '^(HTTP|location)'
# HTTP/2 302
# location: https://example.com/autumn-sale?utm_source=instagram&utm_medium=social&utm_campaign=2026-10-autumn-sale&utm_content=bio-link-2
# HTTP/2 200

If a later hop's location has lost the ?utm_… part, your site's redirect is the problem, not the tags. With a SendURL link this request counts as a click (user agent curl), so expect one extra in the short link's numbers. The redirect checker does the same check from a web page, also as one click, and names the hop that drops the UTM parameters.

3. Watch GA4 receive it. Paste the tagged destination URL (not the short link) into Tag Assistant, which turns on debug mode for that tab, then open Admin → Data display → DebugView (Google's DebugView page). Click the page_view event and check that page_location still carries the UTMs. Use the destination because Tag Assistant adds its own parameter to the URL, and a short link doesn't pass on parameters added to it.

The Traffic acquisition report won't show the session straight away. Check it the next day, once the data has been processed.

Fix past data with a custom channel group

GA4 can't edit values it has already collected, and the default rules are fixed. A custom channel group is the workaround: your own channels, checked in your own order, and per Google's custom channel groups page, they "can be applied to your reports retroactively".

  1. Go to Admin → Data display → Channel groups (you need the Editor role).
  2. Click Create new channel group. It starts as a copy of the default group, so every existing channel is already there.
  3. Name it, for example "Default + fixes".
  4. Click Add new channel, name it (say "QR & print"), click + Add condition group and set Medium exactly matches qr. Click Save channel.
  5. Edit an existing channel to absorb old mistakes. For Email, add a condition group with Medium exactly matches newsletter.
  6. Click Reorder and drag your new channels to the top. Google: "Traffic is included in the first channel whose definition it matches given the current order of channels in the group." Click Apply, then Save group.

In reports and explorations, pick your group as the channel dimension. A standard property can have two custom channel groups with up to 50 channels each, as of 15 October 2026.

The custom group cleans up the past. Fix the links too, using the allowed values in the naming convention guide, or the same mistakes keep arriving.

Checklist

  • Traffic acquisition with Session source / medium added, filtered to Unassigned
  • Each Unassigned pair matched to a cause in the table above
  • Mediums changed to values with a rule: email, social, paid-social, cpc, display, video, sms, audio, affiliate, referral, push
  • Sources not on Google's list (insta, x, threads) paired with utm_medium=social
  • UTMs on the destination URL, before any #, and surviving every redirect (curl -sIL)
  • page_location checked in DebugView for one link per channel
  • A custom channel group for qr and for old values, with the new channels ordered first
  • One person owns the UTM naming convention

Frequently asked questions

Can I change GA4's default channel rules?

No. Google's default channel group page says channel definitions aren't case sensitive and can't be edited. You can create a custom channel group with your own rules and use it in reports, retroactively.

Why is my UTM traffic showing as Direct instead of Unassigned?

Direct means GA4 found no campaign or referrer for the visit, so the UTMs most likely never reached the page. Common reasons: a redirect on the way dropped the query string, the tags were added to a short link instead of its destination, or they sit after a # in the URL.

Is Unassigned traffic bad?

It isn't lost: the sessions, source and medium are all still there. It just isn't in a channel, so channel reports undercount the real ones. Fix the link values going forward and regroup past data with a custom channel group.

Start tracking links free.

Create your first short link with click analytics in seconds. No credit card, no sign-up to start.

Shorten a link