Why GA4 Shows “(not set)”, and How to Work Out Which Cause You Have

By Jackson Kolinski · October 12, 2026 · 9 min read
Why GA4 Shows “(not set)”, and How to Work Out Which Cause You Have

“(not set)” is the least helpful string in GA4. It is not an error, it is not a bug, and it does not mean one thing. It means GA4 had no value for that dimension on that row, and the reason depends entirely on which dimension you are looking at. Some causes are harmless. Some mean a chunk of your reporting is wrong. This is how to tell which one you have.

What it literally means

When GA4 assembles a report, every row is a combination of dimension values. If a row has events that carry no value for a dimension you have asked for, it has to put something in the cell. That something is (not set).

It is worth separating it from two things it gets confused with:

  • (none) is a real value, not an absence. In source/medium, (direct) / (none) means GA4 determined there was no referrer and no campaign, which is a finding.
  • Data thresholding is GA4 withholding rows to protect identity when Google signals are on and the sample is small. That shows as a notice at the top of the report, not as (not set).

Cause 1: the dimension does not apply to the event

This is the most common and the most benign. Many GA4 dimensions are scoped to a session or a user, and you are looking at them next to an event that has no such scope.

Ask for “Landing page” alongside an event that fires without a page view, and you get (not set), because that event never had a landing page attached. The same applies to session-scoped traffic dimensions in an event-scoped report.

How to recognise it: the (not set) rows are confined to specific events, and the equivalent session-scoped report looks clean. Nothing is broken.

Cause 2: a session started without a page_view

GA4 derives the landing page from the first page_view of a session. If a session begins with some other event, there is no first page view to read, so the landing page is unset.

This happens more often than people expect:

  • A session times out while a tab is left open, and the next interaction is a scroll or a click rather than a navigation. GA4 opens a new session on an event that is not a page view.
  • A tag fires custom events before the configuration tag has run, so the earliest event in the session is the custom one.
  • A single-page app sends custom events on route change but never sends a synthetic page_view.

How to recognise it: compare total sessions to sessions with a page view. A persistent gap is this. In BigQuery, the same thing is visible by checking whether each ga_session_id contains a page_view at all.

Cause 3: the referrer was lost in a redirect

This is the one that causes real reporting damage, and it shows up as (not set) or as an inflated direct bucket in source/medium.

Every hop in a redirect chain is a chance to drop the referrer and strip query parameters. A link shortener that redirects through two domains, a geo redirect, a forced www redirect, an old campaign URL that now 301s to a new structure: any of them can arrive at your page with no referrer and no UTMs. GA4 has nothing to attribute to and says so.

How to recognise it: take a campaign URL and follow it with redirects visible, checking at each hop whether the query string survives. If the UTMs are present on the first request and gone by the last, you have found it. The fix belongs to whoever owns the redirect rules, not to the tagging.

The related case: UTMs on the wrong side of a hash

Parameters after a # are not sent to the server and are not read by GA4 as query parameters. A campaign URL built as /page#utm_source=newsletter will arrive with nothing to attribute. It is a small mistake that produces a very large direct bucket.

Cause 4: consent denied

With Consent Mode v2, a visitor who refuses analytics storage still generates pings, but those pings carry no identifiers and little context. GA4 uses them for modelling rather than for row-level reporting, and dimensions that depend on a stored identity come back unset.

How to recognise it: the proportion of (not set) tracks your consent-denial rate, and it changed sharply on the day a new consent banner went live. That date correlation is the giveaway, and it is why a sudden jump in (not set) is worth dating precisely rather than just noticing.

Cause 5: cross-domain tracking is not configured

When a visitor moves from your marketing site to a separate checkout or booking domain without the GA4 linker passing the client ID, the second domain starts a brand-new session. That session's referrer is your own site, which GA4 may classify as a self-referral, or it may end up unset depending on the referral exclusion configuration.

How to recognise it: your own domain appears in the referral list, or conversion sessions have no campaign while the sessions that led to them did. Both point at the same gap.

Cause 6: a custom dimension registered after the fact

Custom dimensions are not retroactive. Register one today and every event from before today reports (not set) for it, permanently. The same happens for events that simply do not send that parameter.

How to recognise it: a clean break on the registration date, with everything before it unset and everything after it populated.

Cause 7: the event arrived without the parameter at all

Dimensions in GA4 are populated from event parameters. If the parameter is absent from the event, the dimension is unset, and this is easy to cause accidentally with a tag manager.

A common version: a GTM tag sends purchase with a transaction_id read from a dataLayer variable. On most pages the variable resolves. On one template it does not, because the developer pushes the order object slightly later. GTM sends the event anyway with the parameter missing, GA4 accepts it, and that slice of purchases reports (not set) forever.

How to recognise it: the unset rows cluster on one template, one device type, or one traffic source rather than spreading evenly. Anything that correlates with a code path rather than with a visitor behaviour points here.

The version that reaches Google Ads

If you import GA4 key events into Google Ads as conversions, the problems above stop being a reporting curiosity and start costing money.

A session with no recoverable source still produces a key event. GA4 records the conversion. But Google Ads can only credit a conversion it can tie back to an ad interaction, so a conversion whose session lost its click identifier simply never appears there. Your GA4 total and your Google Ads total diverge, and the gap is not a reporting artefact: Smart Bidding genuinely never learns about those conversions, and bids accordingly.

This is why a rise in (not set) source/medium is worth treating as urgent rather than cosmetic. The same redirect or consent change that emptied the dimension is also starving the bidding algorithm.

Using BigQuery when the UI will not tell you

The GA4 interface aggregates, which is exactly what you do not want while diagnosing. If the property has the BigQuery export enabled, the raw event table answers questions the UI cannot.

  • Count distinct ga_session_id values that contain no page_view event. That is cause 2, measured rather than guessed.
  • Group the first event of each session by event_name to see what is actually starting sessions.
  • Inspect collected_traffic_sourceon individual events to see what arrived, before GA4's attribution model rewrote it into something session-scoped.

If the export is not enabled, turn it on now even if you do not need it today. It is not retroactive, and the day you want it is the day you most wish you had switched it on three months ago.

A diagnostic order that saves time

  1. Name the dimension.“We have a lot of (not set)” is not a diagnosis. Landing page, source/medium and a custom dimension have three different causes and three different fixes.
  2. Date the change. Chart the share of (not set) over 90 days. A step change points at a deployment, a consent banner or a redirect rule. A flat line that has always been there usually points at scope or a long-standing cross-domain gap.
  3. Check whether sessions have page views. That one comparison separates cause 2 from everything else in a minute.
  4. Walk a campaign URL end to end with redirects visible and watch the query string.
  5. Open DebugView on a real device. Not a desktop with extensions disabled: a phone, on cellular, going through the consent banner the way a visitor does.

When (not set) is the symptom of something worse

A stable background level of (not set) is normal and not worth chasing. What matters is a change in it, because the things that cause a change (a redirect rule, a consent tool, a tag that stopped firing before the config) are the same things that quietly damage the conversion data your bidding depends on. The GA4 report is often the first visible symptom of a problem that is also costing you money in Google Ads and Meta, where it is much harder to see.

That is the argument for watching the shape of your data rather than auditing it occasionally. Taglert monitors data freshness and tracking health across GA4, Meta and Google Ads together and tells you the day something changes, which is usually weeks before anyone notices the reporting drift.

Sources

Stop finding out about broken tracking from your client.

Taglert monitors your pixels and conversion tracking 24/7 and alerts you the moment something breaks.

Start your free trial
  • First scan in ~5 minutes
  • 30-day money-back guarantee
  • No credit card

Related reading