Skip to content
Back to blog
GA4Jul 11, 2026 · Ludde Nyström · 16 min read

GA4 Custom Dimension Showing (not set)? Every Cause and Fix.

GA4 custom dimension showing (not set)? The real causes — registration timing, name mismatches, wrong scope — and the fix for each.

GA4 Custom Dimension Showing (not set)? Every Cause and Fix

You build a report, add a custom dimension as the row, and half the table — or all of it — reads (not set). Nothing crashed, no error banner appeared, and DebugView might even show the parameter arriving in real time while the report insists it has nothing. That gap between "the data is clearly there" and "the report says otherwise" is the whole problem, and it has a specific, findable cause almost every time.

There isn't one bug behind it. It's usually one of seven things, and they don't all get fixed the same way — some are a two-minute Admin correction, one is unfixable for the period it already affected, and one isn't really about the custom dimension at all. Here's how to tell which one you have and what to actually do about it.

Quick answer

(not set) on a GA4 custom dimension means GA4 has no value to show for that dimension on a given row — not hidden, not privacy-masked, just genuinely absent from the data GA4 has to work with. The single most common cause is registration timing: the event parameter was already flowing (or started flowing later) before you registered the custom dimension in GA4's Admin, and GA4 does not backfill — historical rows from before registration stay (not set) permanently, while rows collected after registration populate normally within roughly 24-48 hours. The next most common causes, roughly in order: the event parameter name doesn't exactly match what you registered (case-sensitive, character for character), the dimension is registered at the wrong scope (event vs. user vs. item), or the parameter simply isn't sent on the specific event you're viewing the report against. Confirm which one you actually have in DebugView before changing anything in Admin — it takes about two minutes and rules out half this list immediately.

What Does "(Not Set)" Actually Mean on a Custom Dimension?

Google's own definition is deliberately plain: (not set) is the placeholder label Analytics writes into a report whenever it has no information for a dimension on a given row. It isn't an error state and it isn't GA4 declining to show you something it has — it's a literal statement that no value was found. For a custom dimension specifically, that means the event parameter, user property, or item parameter the dimension is registered to read from was never present in a form GA4's reporting layer could attach to that row.

It's worth being precise about what it isn't, because two other terms get folded into the same mental bucket and shouldn't be. (not provided) is a Universal Analytics-era term for Google encrypting the organic search keyword on a visit, for privacy reasons, before it ever reached your analytics account — a mechanism from search behavior, not your own implementation. GA4 doesn't carry a keyword dimension the same way UA did; organic search terms show up today, if at all, through a linked Search Console property, not through a GA4 dimension Google is withholding from you. If you're picturing (not set) on your own custom dimension as some kind of privacy mask Google applies to data you're not allowed to see, that's the wrong model entirely — nothing about it is a permissions problem, and unlike (not provided), it's almost always something you can actually go fix.

It's also not a blank cell. GA4 doesn't leave an empty space when a dimension has no value for a row — it writes the literal string (not set) every time. That label itself is diagnostic: it's GA4 telling you, specifically, that it looked for a value for that dimension on that row and found none. The rest of this piece is about figuring out why.

One more scoping note before the causes: this piece is specifically about a custom dimension you built showing (not set) — a parameter you registered yourself. If what's actually showing (not set) is a built-in dimension like Session source / medium, across sessions generally rather than on one dimension you added, that's a different mechanism with its own root causes, most often a missing session_start event. Our session_start troubleshooting guide covers that symptom specifically.

Why This Happens: 7 Causes, Roughly Ordered by How Often They Show Up

Work through these in order — the first two or three account for the large majority of "why is my custom dimension not set" reports we see, and confirming or ruling out the earlier ones is usually fast.

1. You Registered the Dimension After the Data Was Already Being Collected

Cause: This is the one people run into constantly and it isn't a bug — it's how GA4's registration model works. A custom dimension only starts reporting on data collected after you register it in the GA4 Admin. There's no retroactive step: if you were already sending an event parameter for a week before you registered the matching custom dimension, that week is gone from the dimension's perspective — every row from that period will read (not set) for it, permanently, in the GA4 reporting interface. The same applies, for the same underlying reason, if the parameter itself simply didn't exist in your implementation yet during that period — there's nothing to backfill because nothing was ever sent.

Fix: Going forward, this fixes itself the moment you register the dimension — there's nothing further to do except wait out the processing window (cause #4, below). For the historical gap, there is genuinely no undo inside the GA4 UI. The one real exception: if BigQuery export was already linked during that period, the raw parameter value is sitting in event_params regardless of whether you'd registered a GA4 custom dimension for it yet — registration only controls what the GA4 reporting interface surfaces, not what BigQuery already captured. A query like this recovers real values for a period the GA4 UI will always show as (not set):

SELECT
  (SELECT value.string_value FROM UNNEST(event_params)
   WHERE key = 'content_group') AS content_group,
  COUNT(*) AS event_count
FROM `your-project.analytics_XXXXXXXXX.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20260101' AND '20260630'
GROUP BY content_group
ORDER BY event_count DESC;

That only works if the parameter was genuinely being sent during the period in question and BigQuery was already linked — it can't recover data that was never collected in the first place. If neither condition holds, treat the historical gap as permanent and move on; re-litigating it doesn't change the outcome.

2. The Event Parameter Name Doesn't Exactly Match What You Registered

Cause: The Event parameter (or User property) field you typed when registering the dimension has to match the parameter name arriving from GTM or gtag.js exactly — including case. GA4 treats this as a literal string match, not a fuzzy or case-insensitive one. contentGroup, content_group, and Content_Group are three different parameter names as far as registration is concerned, and if your event is sending one of them while the dimension is registered against another, the dimension will show (not set) forever — not because the data is missing, but because GA4 is looking for a key that never arrives under that exact spelling.

Fix: Open DebugView, trigger the event, and read the parameter name directly off the raw parameter list — not off a GTM variable name, a spec doc, or however someone described it in Slack six months ago. Compare that exact string, character by character, against the Event parameter field in Admin → Data display → Custom definitions → Custom dimensions. A stray capital letter or an extra underscore is the entire bug. If they don't match, you generally can't edit the field in place — both Scope and the parameter/property field are locked after you save a dimension — so archive the mismatched one and register a fresh dimension with the corrected name.

3. Wrong Scope — Event vs. User vs. Item

Cause: GA4 registers a custom dimension against one of three scopes, and each one reads from a different place in the data. Event-scoped reads a parameter directly off the event. User-scoped reads a user property, which is set separately (via gtag('set', 'user_properties', …) or a dedicated GTM tag) and persists across events rather than living on any single one. Item-scoped reads a parameter nested inside the items array of an ecommerce event — item_color or item_size on a purchase or add_to_cart, for example — which is a structurally different location from the top level of the event entirely. Register the dimension at the wrong scope for where the value actually lives, and GA4 looks in the wrong place every time: an item-level parameter registered as event-scoped, or a value only ever set as a user property but registered as event-scoped, both produce a clean, consistent (not set) — the setup looks correct at a glance, which is exactly why this one is easy to miss.

Fix: In DebugView, check where the parameter actually appears — under the event's own parameter list, under user properties, or nested inside an item in the ecommerce items array — and register (or re-register) the dimension at the matching scope. As with a name mismatch, scope can't be changed after saving, so a wrong-scope dimension needs to be archived and recreated correctly rather than edited.

4. You Just Registered It — Give It 24-48 Hours

Cause: Even when registration timing and scope are both correct, a freshly created custom dimension doesn't populate instantly. Google's own guidance is that it can take roughly 24-48 hours after registration before you can reliably report on the dimension in the standard interface. If you registered it an hour ago and it's showing (not set) across the board, that's very likely just processing lag, not a real problem.

Fix: Don't touch the Admin configuration again while you wait — re-archiving and recreating a dimension that's simply mid-processing resets the clock for no reason. Confirm the parameter is actually arriving correctly in DebugView or Realtime first (cause #4 only explains a delay in the standard report, not in DebugView, which reflects live hits immediately). If it looks right there, give it the full processing window before troubleshooting further — and if it's still (not set) in every report after 48 hours have genuinely passed, move down this list; you're looking at one of the other six causes instead.

5. The Parameter Isn't Actually Sent on the Event You're Viewing

Cause: A custom dimension isn't a property of your whole property — it only has a value on the specific events that actually carry that parameter. If content_group is only ever sent on page_view, and you build a report or exploration breaking down purchase events by content_group, every single purchase row will correctly show (not set) — not because anything is broken, but because that event genuinely never carried the parameter. This is one of the most common false alarms in this list: the dimension is registered correctly, the parameter is firing correctly, and the report is technically accurate — it's just being asked a question the data was never designed to answer.

Fix: In DebugView, click through several different event types on a real test session and check whether the parameter actually appears on the one you're reporting against. If it's genuinely missing from that event, you have two real options: add the parameter to that event's tag (in GTM or your gtag calls) if it should logically be there, or adjust which event you're segmenting the report by. Don't assume the dimension itself is broken until you've confirmed which events actually send it.

6. Cardinality — Your Values Collapsed Into (Other), Not (Not Set)

Cause: This one is a different mechanism entirely that gets mistaken for (not set) because it also produces a bucket row instead of a clean value. GA4 defines a high-cardinality dimension as one with more than 500 unique values in a single day, and the platform has a hard cardinality limit of 50,000 values — past that, a report hits its row limit and condenses the overflow into an (other) row, not (not set). A custom dimension built to carry something like a raw search query, a session ID, or a full product SKU list is a classic way to hit this: every individual value is technically real and present, but there are too many distinct ones for the report table to display each on its own row.

Worth separating clearly from GA4's other (other) mechanism: Google Signals-driven privacy thresholding, which we cover in full in our guide to GA4 thresholding, is a completely different cause that happens to produce the same-looking label. Thresholding is about protecting individually-identifiable users and has no fixed numeric trigger; cardinality collapse is a plain table-size limit with the specific 500-value and 50,000-value thresholds above, and has nothing to do with Google Signals or privacy at all. If you're chasing an (other) row on a high-cardinality custom dimension, switching your Reporting Identity setting — the usual thresholding fix — won't do anything, because that's not the mechanism producing it here.

Fix: Don't register a custom dimension for a value with unbounded or near-unbounded cardinality — group it into buckets before it reaches GA4 (price ranges instead of exact prices, query categories instead of raw search strings) or, if you genuinely need every distinct value, query it in BigQuery, which isn't subject to the same report-table row limit.

Registration problems are checkable before you go hunting

One of NiceLookingData's 61 GA4 checks flags custom dimensions approaching the 50/25 capacity ceiling, duplicate parameter registrations quietly wasting a slot, and dimensions whose values have grown high-cardinality enough to be collapsing into (other) rows. It won't tell you that one specific report is showing (not set) right now — that's a report-level check only DebugView and the report itself can answer — but it will catch the underlying registration setup problems most likely to be the reason before you spend time digging. Run a free GA4 audit.

7. You Hit the Registration Limit and the Dimension Was Never Actually Created

Cause: Standard GA4 properties support 50 event-scoped, 25 user-scoped, and 10 item-scoped custom dimensions (125 / 100 / 25 on GA4 360). If a property is already at capacity in the relevant scope, an attempt to register one more dimension in that scope fails — and it's easy to assume it saved when it didn't, especially if someone else on the team did the registering and reported back that it was "done." Every event downstream still shows (not set) for the intended dimension, because there's no dimension to attach the data to at all.

Fix: Check Admin → Data display → Custom definitions → Custom dimensions directly and confirm the dimension actually exists — don't take a secondhand "yes, that's set up" at face value. If the relevant scope is at or near its ceiling, archive dimensions that haven't received data recently to free capacity before creating the new one; archiving isn't instant, so don't create-and-archive in the same sitting and expect the slot to be immediately available. For the full mechanics of scopes, limits, and naming conventions beyond this specific symptom, see our complete guide to GA4 custom dimensions.

How to Confirm Which Cause You Have — Before You Touch Any Admin Setting

Changing Admin configuration before you know which of the seven causes you're looking at is how a five-minute fix turns into a week of guessing. Confirm first.

Start With DebugView, Not the Report

Open Admin → DebugView (pair it with the Google Analytics Debugger browser extension, or GTM Preview, so your session is flagged as a debug session) and trigger the event you care about on a real test visit. Click into that event in the timeline and read its raw parameter list. You're checking three things at once: is the parameter present at all on this event (rules cause #5 in or out), what is its exact spelling including case (rules cause #2 in or out), and is it sitting at the event level, under user properties, or nested in an item (rules cause #3 in or out). This single check resolves three of the seven causes in about two minutes, without waiting on a report to process.

Cross-Check in Realtime

If DebugView looks correct but the standard report still shows (not set), open Reports → Realtime and look for the same parameter there. Realtime reflects live traffic within seconds to minutes, well ahead of standard reports, so it's a useful middle checkpoint between "DebugView says it's firing" and "the report hasn't caught up yet" (cause #4) versus "the report is correctly showing no historical value because of when the dimension was registered" (cause #1).

If All of That Looks Right, Check the Admin Configuration Itself

Parameter arriving correctly in DebugView, correct scope, correct spelling, and still (not set) after 48 hours genuinely points at causes #1 or #7: either the dimension was registered after this data started flowing (permanent, historical only — new data should be fine) or it was never successfully registered in the first place because a scope was already at capacity. Go to Admin → Data display → Custom definitions → Custom dimensions and confirm the dimension exists, its scope, and its exact registered parameter name against what you just saw in DebugView.

A custom dimension reading (not set) is one specific, narrow symptom out of a much longer list of ways GA4 data can look wrong. If what actually brought you here is a different pattern entirely — traffic that looks off, conversions that stopped counting, numbers that don't reconcile with another tool — our symptom-by-symptom GA4 diagnosis tool works the same way this piece does: most likely cause first, in order, instead of a generic troubleshooting checklist.

Frequently Asked Questions

What does (not set) mean for a GA4 custom dimension?

It means GA4 found no value for that dimension on that particular row — the placeholder is a literal statement of absence, not an error or a privacy mask. For a custom dimension specifically, it means the underlying event parameter, user property, or item parameter the dimension reads from wasn't present in a form GA4 could attach to that row: it could be missing from that specific event, sent under a different parameter name, sent at the wrong scope, or simply predate when you registered the dimension. It's a different mechanism from the Universal-Analytics-era term "(not provided)," which referred to Google withholding organic search keywords for privacy reasons and has no real equivalent for your own custom dimension data.

Does GA4 backfill custom dimensions after you register them?

No. Custom dimension registration only applies to data collected from the point of registration forward — GA4 does not retroactively populate a dimension for events collected before you created it in Admin, even if the underlying event parameter was already being sent during that earlier period. That gap in reporting is permanent inside the standard GA4 interface. The one workaround: if BigQuery export was already linked during that historical window, the raw parameter value is still sitting in the exported event_params data regardless of GA4 UI registration status, and you can query it directly in BigQuery even though the GA4 reports will always show (not set) for that period.

Why does my custom dimension parameter name matter so much — is it really case-sensitive?

Yes. The Event parameter or User property field you enter when registering a custom dimension in GA4 Admin must match the parameter name arriving from your implementation exactly, including case — GA4 treats it as a literal string match, not a case-insensitive or fuzzy one. page_type and Page_Type are two different parameter names as far as registration is concerned. If they don't match, the dimension will show (not set) indefinitely, and because both the scope and the parameter name fields lock after you save a dimension, the fix is usually to archive the mismatched dimension and register a corrected one rather than edit it in place.

What's the difference between (not set) and (other) in GA4?

(not set) means GA4 found no value at all for that dimension on that row. (other) means values genuinely exist, but there are too many distinct ones for the report table to list individually, so the overflow past the row limit gets condensed into a single catch-all row — GA4 treats any dimension with more than 500 unique values in a day as high-cardinality, with a hard cardinality limit of 50,000 values before this kicks in. They're easy to conflate because both show up as an unhelpful bucket instead of a real value, but the fixes are different: a missing value is a registration, naming, scope, or timing problem, while an (other) row from cardinality is a data-shape problem — usually solved by grouping raw values into buckets before they reach GA4, or querying BigQuery directly instead of the standard report. Note that GA4 also produces an (other) row through a separate mechanism, Google Signals privacy thresholding, which has nothing to do with cardinality and needs a different fix — see our guide to GA4 thresholding for that case specifically.

Written by
Ludde Nyström — Founder, NiceLookingData

Analytics consultant turned founder. After years running the same GA4 and GTM audits across client engagements, Ludde built the audit into a product — so the pattern-matching takes a minute, not a meeting. More about Ludde →

Free tool

Run a free GA4 audit.

Connect your Google Analytics 4 property. Our auditor runs 61 checks and gives you an instant health score with a plain-English action plan.

Thanks for reading.