Blog

405 nunca apaga credencial: o erro transitório que a gente tratou como terminal

7 de julho de 2026 · Time Orion

Toda integração com serviço externo enfrenta a mesma pergunta na hora do erro: isso é temporário ou definitivo?

Errar para o lado do "temporário" custa uma tentativa a mais. Errar para o lado do "definitivo" pode custar a credencial — e, com ela, o serviço inteiro até alguém reconectar à mão.

Aprendemos isso duas vezes, do jeito caro.

O que aconteceu

Numa integração de mensageria que mantém uma conexão persistente, o servidor às vezes respondia com um código de erro na tentativa de reconectar. A leitura intuitiva era: a sessão não vale mais, limpe as credenciais e peça um novo pareamento.

Foi o que o código fazia. E estava errado.

O erro era transitório — infraestrutura do outro lado, não invalidação de sessão. Ao apagar as credenciais, destruíamos uma sessão perfeitamente boa. Pior: nesse protocolo, a sessão não se recupera de um apagamento. Não há como voltar atrás; alguém precisa parear de novo, fisicamente, com o aparelho na mão.

O incidente aconteceu duas vezes antes de a causa ficar clara. A segunda foi o que transformou uma correção pontual numa regra escrita em maiúsculas no código.

A regra

Só um erro que diz explicitamente que a sessão terminou é terminal. Todo o resto é transitório: espera, backoff, tenta de novo.

Na prática, isso significa uma lista curta e explícita de códigos que autorizam limpar credencial — e um comportamento padrão de não limpar para tudo que não está na lista. O default é a alternativa segura, não a alternativa óbvia.

Repare que é o inverso do reflexo comum. O reflexo é enumerar os erros transitórios e tratar o resto como fatal. Quando a consequência do fatal é irreversível, a enumeração tem que ser do lado fatal.

O princípio geral

A pergunta que essa história ensinou a fazer, em qualquer integração:

Qual erro deste lado tem consequência irreversível?

Achou? Então a lista de condições que dispara essa consequência precisa ser explícita, curta e conservadora. E o caminho não enumerado tem que ser o que preserva estado.

Aplicações do mesmo raciocínio no nosso código:

  • Retry só em leitura. Repetir um GET que falhou é inofensivo. Repetir um POST pode duplicar a mensagem que já foi entregue ao destinatário. Nosso cliente HTTP só refaz requisições idempotentes — e essa foi outra lição de campo.
  • Sempre 200 depois de autenticado. Um webhook que devolve erro pode ser desabilitado pelo provedor. Depois de validar a assinatura, responda 200 e trate o problema do seu lado.
  • Proxy caiu, canal offline. Uma conexão configurada para sair por um proxy específico não deve cair para conexão direta quando o proxy falha. Parece resiliência; é vazamento de identidade de rede — e, nesse caso, risco de banimento.

O padrão por trás dos três

Nos três, a decisão é sobre qual erro é aceitável: o de indisponibilidade ou o de estado corrompido.

Indisponibilidade é chata e reversível — o serviço volta. Estado corrompido é silencioso e caro: a mensagem duplicada já chegou, a sessão já morreu, o IP já apareceu.

Quando o par é esse, o sistema deve preferir ficar indisponível.

O que fica no código

Duas coisas, e a segunda importa tanto quanto a primeira:

  1. A lista explícita de erros terminais.
  2. O comentário contando o incidente. "Dois incidentes provaram que este código é transitório; auto-limpeza destruía sessões boas e este protocolo não se recupera disso." Sem ele, daqui a um ano alguém olha o tratamento conservador, acha exagero e "simplifica".

Comentário que documenta o porquê é a única defesa contra a próxima pessoa razoável.

O resumo

Antes de tratar um erro como definitivo, pergunte o que acontece se você estiver errado. Se a resposta for irreversível, inverta o default: enumere o fatal, presuma o transitório e escreva no código a história que justificou a escolha.