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.
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 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.
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.
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 }}
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.
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.