(not set) é o valor que o GA4 usa quando não recebeu informação nenhuma para aquela dimensão. Não é uma fonte de tráfego, nem um canal. É um buraco no dado com nome de categoria.
Três valores parecidos costumam ser confundidos com ele, e cada um pede uma investigação diferente:
| Valor | O que significa | É problema? |
|---|---|---|
(not set) | O GA4 não recebeu o dado para a dimensão | Sim. É o objeto deste guia |
(direct) / (none) | O GA4 recebeu a sessão, mas sem referência nem parâmetro de campanha | Às vezes. Direto alto demais costuma esconder UTM perdida |
(other) | O relatório agrupou valores por limite de linhas (cardinalidade) | Não é perda de dado. No BigQuery o valor real aparece |
(data not available) | O dado não está disponível para aquele período ou combinação | Depende. Leia a documentação antes de investigar |
O segundo cuidado é o escopo. "(not set)" na origem da sessão, na origem do primeiro usuário, na página de entrada e na campanha do Google Ads são quatro problemas diferentes. Comece pela origem e mídia da sessão, que é a que pesa nas decisões de canal.
Antes de corrigir qualquer coisa, tire três números: o percentual de sessões, de conversões e de receita em (not set). O segundo e o terceiro importam mais que o primeiro. Uma operação com 15% das sessões em (not set) e 2% da receita tem outro problema, e outra urgência, do que uma com 5% das sessões e 30% da receita.
A consulta abaixo monta uma tabela de sessões a partir do export do GA4 e já marca os sinais que vamos usar nas próximas seções. Troque o dataset pelo seu.
-- Base de sessões com os sinais de diagnóstico (export do GA4 no BigQuery)
-- Troque `projeto.analytics_123456789` pelo dataset da sua propriedade
CREATE OR REPLACE TABLE `projeto.auditoria.sessoes_notset` AS
WITH eventos AS (
SELECT
event_date,
user_pseudo_id,
(SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') AS session_id,
event_name,
event_timestamp,
platform,
stream_id,
device.category AS device_category,
privacy_info.analytics_storage AS analytics_storage,
collected_traffic_source.manual_source AS utm_source,
collected_traffic_source.manual_medium AS utm_medium,
collected_traffic_source.gclid AS gclid,
session_traffic_source_last_click.cross_channel_campaign.source AS sessao_source,
session_traffic_source_last_click.cross_channel_campaign.medium AS sessao_medium,
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') AS page_location,
ecommerce.purchase_revenue AS receita
FROM `projeto.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 28 DAY))
AND FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY))
)
SELECT
user_pseudo_id,
session_id,
MIN(PARSE_DATE('%Y%m%d', event_date)) AS dia,
ARRAY_AGG(event_name ORDER BY event_timestamp LIMIT 1)[OFFSET(0)] AS primeiro_evento,
COUNT(*) AS n_eventos,
LOGICAL_OR(event_name = 'session_start') AS tem_session_start,
LOGICAL_OR(event_name IN ('page_view', 'screen_view')) AS tem_pageview,
LOGICAL_OR(gclid IS NOT NULL) AS tem_gclid,
LOGICAL_OR(utm_source IS NOT NULL AND utm_medium IS NULL) AS utm_incompleta,
ANY_VALUE(platform) AS platform,
ANY_VALUE(stream_id) AS stream_id,
ANY_VALUE(device_category) AS device_category,
MAX(analytics_storage) AS analytics_storage,
MAX(sessao_source) AS sessao_source,
MAX(sessao_medium) AS sessao_medium,
MAX(NET.HOST(page_location)) AS host,
COUNTIF(event_name = 'purchase') AS compras,
SUM(IF(event_name = 'purchase', receita, 0)) AS receita
FROM eventos
WHERE user_pseudo_id IS NOT NULL AND session_id IS NOT NULL
GROUP BY 1, 2;
session_traffic_source_last_click entrou no export em 2024. Em exports mais antigos, ou se ele vier vazio, reconstrua a origem da sessão com collected_traffic_source e o parâmetro page_referrer do primeiro evento.Com a tabela pronta, o número de partida:
-- Ponto de partida: quanto do dado está sem origem
SELECT
COUNT(*) AS sessoes,
ROUND(100 * COUNTIF(IFNULL(sessao_source, '(not set)') = '(not set)') / COUNT(*), 1) AS pct_sessoes_not_set,
ROUND(100 * SUM(IF(IFNULL(sessao_source, '(not set)') = '(not set)', compras, 0)) / NULLIF(SUM(compras), 0), 1) AS pct_compras_not_set,
ROUND(100 * SUM(IF(IFNULL(sessao_source, '(not set)') = '(not set)', receita, 0)) / NULLIF(SUM(receita), 0), 1) AS pct_receita_not_set
FROM `projeto.auditoria.sessoes_notset`;
Guarde esse resultado com a data. Ele é a linha de base da seção 6.
(not set) quase nunca vem de um erro só. Ele é a soma de pequenas perdas, cada uma com um dono diferente: mídia, desenvolvimento, CRM, jurídico, produto. Por isso a investigação vai por família, da mais comum para a mais trabalhosa.
tem_gclid = true. Teste a URL final com o parâmetro e veja se ele sobrevive ao redirect: curl -sIL "https://seusite.com.br/lp?gclid=teste123" | grep -i locationutm_source e sem utm_medium. Fontes duplicadas por grafia: "Instagram", "instagram", "ig".utm_incompleta = true, e a lista de valores distintos de source e medium dos últimos 90 dias.page_view nem session_start. Aparecem user_engagement, scroll, eventos de vídeo ou eventos próprios.primeiro_evento, e a consulta 4.2.client_id e o session_id do navegador. O GA4 recebe a venda, mas não sabe de qual sessão ela veio.client_id (cookie _ga) e o session_id (cookie _ga_<ID>) no checkout, guarde junto do pedido e envie os dois no evento de servidor.analytics_storage = 'No', eventos sem user_pseudo_id, quedas que coincidem com mudanças no banner de cookies.ad_storage virou o único portão do dado entre GA4 e Google Ads. Banner que não envia o sinal certo apaga conversão sem avisar.ad_storage, ad_user_data e ad_personalization mapeados. Valide com o jurídico, não só com o time de mídia.host.user_id para costurar a jornada.platform = 'ANDROID' ou 'IOS'.stream_id.A consulta que mais rende. Ela cruza os sinais da tabela de sessões e mostra as combinações com mais sessões sem origem. Em geral, duas ou três linhas explicam a maior parte do problema.
SELECT
primeiro_evento, tem_session_start, tem_pageview, tem_gclid, utm_incompleta,
platform, analytics_storage, host,
COUNT(*) AS sessoes,
COUNTIF(IFNULL(sessao_source, '(not set)') = '(not set)') AS sessoes_not_set,
ROUND(100 * COUNTIF(IFNULL(sessao_source, '(not set)') = '(not set)') / COUNT(*), 1) AS pct_not_set,
SUM(compras) AS compras
FROM `projeto.auditoria.sessoes_notset`
GROUP BY ALL
HAVING sessoes >= 50
ORDER BY sessoes_not_set DESC
LIMIT 30;
SELECT
primeiro_evento,
COUNT(*) AS sessoes,
ROUND(100 * COUNTIF(IFNULL(sessao_source, '(not set)') = '(not set)') / COUNT(*), 1) AS pct_not_set
FROM `projeto.auditoria.sessoes_notset`
GROUP BY 1
ORDER BY sessoes DESC;
Se page_view e session_start não dominarem o topo, a família C está pesando.
-- Compras sem session_id e compras em sessões de um evento só
WITH compras AS (
SELECT
user_pseudo_id,
(SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') AS session_id
FROM `projeto.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 28 DAY))
AND FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY))
AND event_name = 'purchase'
)
SELECT
COUNT(*) AS compras,
COUNTIF(c.session_id IS NULL) AS compras_sem_sessao,
COUNTIF(s.n_eventos = 1) AS compras_em_sessao_de_1_evento
FROM compras c
LEFT JOIN `projeto.auditoria.sessoes_notset` s
ON s.user_pseudo_id = c.user_pseudo_id AND s.session_id = c.session_id;
SELECT
PARSE_DATE('%Y%m%d', event_date) AS dia,
COUNT(*) AS eventos,
ROUND(100 * COUNTIF(privacy_info.analytics_storage = 'No') / COUNT(*), 1) AS pct_analytics_negado,
ROUND(100 * COUNTIF(user_pseudo_id IS NULL) / COUNT(*), 1) AS pct_sem_identificador
FROM `projeto.analytics_123456789.events_*`
WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY))
AND FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY))
GROUP BY 1
ORDER BY 1;
Uma mudança brusca nessa série quase sempre tem uma data de publicação do banner por trás.
Com as causas na mesa, a tentação é corrigir a mais fácil. A ordem certa é outra: a família que concentra mais receita em (not set) vem primeiro, mesmo que dê mais trabalho.
| Família | Peso típico | Esforço | Quem resolve |
|---|---|---|---|
| A. Google Ads e gclid | Alto em operação com muita mídia de busca | Baixo | Mídia + desenvolvimento |
| B. UTM | Médio, cresce com CRM e influência | Baixo, mas contínuo | CRM, mídia e conteúdo |
| C. Sessão sem page_view | Médio | Médio | Analytics + desenvolvimento |
| D. Evento de servidor | Alto nas conversões | Médio | Desenvolvimento backend |
| E. Consentimento | Varia muito | Médio | Analytics + jurídico |
| F. Domínios e checkout | Alto em e-commerce com gateway externo | Baixo a médio | Analytics |
| G. App e site | Alto em operação com app | Médio | Analytics + produto |
A melhoria tem que aparecer numa série, não numa captura de tela. Rode a consulta abaixo toda semana e anote no gráfico a data de cada correção.
SELECT
dia,
COUNT(*) AS sessoes,
ROUND(100 * COUNTIF(IFNULL(sessao_source, '(not set)') = '(not set)') / COUNT(*), 1) AS pct_not_set,
ROUND(100 * SUM(IF(IFNULL(sessao_source, '(not set)') = '(not set)', compras, 0)) / NULLIF(SUM(compras), 0), 1) AS pct_compras_not_set
FROM `projeto.auditoria.sessoes_notset`
GROUP BY 1
ORDER BY 1;
Duas checagens evitam a melhoria falsa:
Quer guardar para consultar depois? O guia completo em PDF, com todas as consultas.
Baixar o PDF ↓30 minutos, de graça. Entro no seu GA4 e mostro de onde vem o tráfego sem origem.
Quero meu diagnóstico grátis →