BUGFIXJAVASCRIPTEXTENSÃO

v1.0.2: ReferenceError silencioso que só aparecia em produção

Um bug de escopo de variável em JavaScript que não aparecia em dev, mas derrubava a recuperação de timeout da IA em produção.

07 de agosto de 2026·4 min de leitura

Em 07 de agosto de 2026, a versão 1.0.2 da extensão foi publicada com um único commit: mover uma linha de código dois blocos para cima. Mas a razão por trás dessa linha vale um post.

O bug

O console do navegador mostrava o seguinte erro ao usar a funcionalidade de sugestão de respostas da IA:

Uncaught (in promise) ReferenceError: isTimeout is not defined

O erro só acontecia em um cenário específico: quando o servidor da IA demorava mais de 45 segundos para responder e o AbortController disparava o timeout. Em uso normal, sem timeout, tudo funcionava perfeitamente. Por isso passou despercebido.

A causa raiz

O código usava a seguinte estrutura para distinguir um timeout real de um abort intencional (como quando o usuário troca de chat enquanto a IA processa):

try { let isTimeout = false const timeoutId = setTimeout(() => { isTimeout = true abortController.abort() }), 45000) await fetch(...) } catch (e) { if (e.name === 'AbortError' && isTimeout) { // aqui explode }}

Em JavaScript, let e const têm escopo de bloco. O bloco do try e o bloco do catch são blocos separados — eles não compartilham o mesmo escopo léxico. Isso significa que isTimeout, declarada dentro do try, simplesmente não existe quando o motor do JavaScript entra no catch.

O motivo de só aparecer em produção e não em desenvolvimento: em dev, o servidor de IA responde rápido (latência de rede local + servidor quente). O timeout de 45 segundos nunca disparava nos testes. Em produção, usuários com conexão ruim ou em horários de pico do servidor atingiam o timeout real, e o catch tentava ler uma variável que não existia no seu escopo.

O fix

A correção é trivial: declarar isTimeout antes do bloco try, no escopo da função, onde tanto o try quanto o catch conseguem enxergá-la:

let isTimeout = false try { const timeoutId = setTimeout(() => { isTimeout = true abortController.abort() }), 45000) await fetch(...) } catch (e) { if (e.name === 'AbortError' && isTimeout) { // funciona }}

O que aprendemos

Dois pontos práticos que ficam desse bug:

Primeiro: sempre declare flags de estado compartilhado entre try/catch no escopo externo. É uma regra simples, mas fácil de esquecer quando você está no meio de uma lógica de cancelamento assíncrono.

Segundo: bugs que só aparecem sob condições de latência alta são os mais traiçoeiros. Eles passam em todos os testes locais, chegam em produção silenciosos, e só surgem quando o usuário mais precisa que a ferramenta funcione. A solução de longo prazo é ter testes que simulam timeout forçado — algo que adicionamos ao backlog.

Impacto em produção

Usuários que atingiam o timeout de 45s viam o spinner da IA travado indefinidamente, sem mensagem de erro e sem possibilidade de tentar novamente (já que o estado de loadingnunca era resetado pelo caminho com erro). Na 1.0.2, o timeout exibe a mensagem correta:"A IA demorou demais para responder. Tente novamente." e libera o botão.

← TODOS OS POSTSzapbooster.com.br/blog