Pular para o conteúdo
Lebtaglebtag
Publicado em5 min de leitura

O bug do gtag que descarta todos os seus eventos em silêncio

O script carrega, o dataLayer enche, o console não acusa nada — e nenhum evento chega ao GA4. A causa é uma linha que quase toda implementação moderna escreve errado.

  • Analytics
  • GA4
  • JavaScript
  • Depuração

Existe uma classe de bug pior que o erro que quebra a página: o que não quebra nada. O script do Google Analytics carrega normalmente, o array dataLayer enche a cada interação, o console fica limpo — e nenhum evento aparece no relatório. Você passa dias procurando problema de configuração, de propriedade, de bloqueador de anúncio. O problema está numa linha de JavaScript.

O sintoma

Você abre o console, inspeciona o dataLayer e vê os eventos lá dentro. Tudo indica que a instrumentação funciona. No painel do GA4, o Realtime não mostra nada. Nenhuma requisição sai para o endpoint de coleta. Nenhum erro é lançado.

É esse silêncio que engana. Um erro visível você conserta em minutos; um descarte silencioso pode custar semanas de dado perdido antes de alguém notar.

A causa

O snippet oficial do Google declara a função gtag assim:

window.dataLayer = window.dataLayer || [];
function gtag(){ dataLayer.push(arguments); }

Repare em duas coisas incomuns: a função não declara nenhum parâmetro, e empurra arguments — o objeto especial que toda função JavaScript não-arrow recebe. Isso parece um detalhe de estilo antigo. Não é.

Quem moderniza esse código costuma escrever a versão que parece equivalente:

// ERRADO: parece igual, e descarta tudo
const gtag = (...args) => dataLayer.push(args);

A diferença: o rest parameter args é um Array de verdade. O arguments do snippet original é um objeto array-like — parecido com array, mas não é um. E o gtag.js só processa itens do dataLayer que sejam objetos arguments. Qualquer outra coisa é ignorada. Sem erro, sem aviso, sem log.

Por que o Google fez assim

O gtag.js precisa distinguir dois tipos de item no mesmo array: comandos que ele deve executar e dados que outras ferramentas colocaram ali. O dataLayer é compartilhado com o Google Tag Manager e com qualquer script da página. Usar a assinatura de arguments como marcador é o jeito de dizer "isto aqui é um comando meu", sem depender de convenção de nome que qualquer um poderia imitar.

A consequência prática é que a forma da função faz parte do contrato. Ela não é estilo — é protocolo.

A correção

Mantenha a declaração exatamente como o Google publica, e resolva a tipagem por fora. Em TypeScript:

declare global {
  interface Window {
    dataLayer: unknown[];
    gtag: (...args: unknown[]) => void;
  }
}

// a implementação NÃO declara parâmetros
window.dataLayer = window.dataLayer || [];
function gtag() {
  // eslint-disable-next-line prefer-rest-params
  window.dataLayer.push(arguments);
}

A assinatura pública vem da anotação de tipo; a implementação continua sendo a do snippet. É feio para quem gosta de código moderno, e é o que funciona.

Como confirmar que está certo

Não confie no dataLayer cheio — ele enche dos dois jeitos. Confie na rede:

  1. Abra a aba Network do navegador e filtre por collect ou por google-analytics.
  2. Dispare um evento na página.
  3. Uma requisição deve sair para o endpoint de coleta. Se o dataLayer cresce e nenhuma requisição aparece, é este bug.
  4. Confirme também no Realtime do GA4, que é o único relatório sem atraso de processamento.

As outras três causas do mesmo sintoma

Quando a implementação do gtag está correta e ainda assim não chega evento, a lista de suspeitos é curta:

  • Consentimento negado ou ainda não respondido. Em implementação com opt-in de verdade, nada é enviado antes do aceite — e isso é o comportamento esperado, não um defeito.
  • Bloqueador de anúncio. Testar em janela anônima sem extensões elimina a dúvida em dez segundos.
  • Content Security Policy sem os domínios do Google. Aqui o console reclama, então é o mais fácil dos três.
A regra que fica: dataLayer cheio não é prova de nada. Prova é requisição saindo.

Por que escrevemos isto

Este bug nos custou tempo num projeto em produção, e a solução não estava em nenhum lugar óbvio — está implícita na forma do snippet oficial, que quase todo tutorial reescreve "melhorado". É o tipo de armadilha que só aparece para quem instrumenta analytics a sério, e por isso vale estar escrito.