Repository navigation
Cliente morre após stream:error <ack class="status" type="media"/> e nunca reconecta; connect vira no-op e forcereconnect dá 500 até reiniciar o processo (0.7.2) #185
Description
Ambiente
- Imagem:
evoapicloud/evolution-go:latest(v0.7.2), Docker Swarm, 1 réplica - Store: Postgres (schema whatsmeow v14),
DATABASE_SAVE_MESSAGES=false - ~15 instâncias ativas, webhook com subscribe
MESSAGE,SEND_MESSAGE,CONNECTION,QRCODE - Instâncias criadas com
ignoreGroups=true,ignoreStatus=true - Produção real (CRM de atendimento) — cada hora de queda é lead perdido
Resumo
Tivemos 3 ocorrências em 5 dias (27, 30 e 31/08/2026) do mesmo quadro, em instâncias diferentes: o cliente whatsmeow recebe um stream:error desconhecido com <ack class="status" type="media"/>, o socket cai (EOF) e o cliente nunca mais reconecta. A instância fica com disconnect_reason = "Reconnecting" para sempre, e as rotas de recuperação ficam todas inertes:
| Rota | Resposta durante o travamento |
|---|---|
POST /instance/connect (token da instância) |
200, mas no-op silencioso — o cliente morto não é recriado; nenhuma linha de log |
POST /instance/forcereconnect/{id} (chave global) |
500 {"error":"failed to connect"} em ~2,0s, sem nenhuma linha de log |
POST /instance/disconnect |
500 {"error":"no active session found"} |
GET /instance/status |
{Connected:false, LoggedIn:false} |
O único destrave é reiniciar o processo (docker service update --force). Depois do restart, um único POST /instance/connect sobe a MESMA sessão em ~5s, sem QR — ou seja, credencial e device store estavam íntegros o tempo todo. O defeito é estado interno do processo.
Na ocorrência de 30–31/08 a instância ficou 27 horas fora do ar; um watchdog externo disparou 693 POST /instance/connect (todos 200) sem nenhum efeito. Uma segunda instância travou junto no mesmo período, com o mesmo quadro.
Postgres não estava esgotado: 0 ocorrências de too many clients nos logs de 96h e 9/100 conexões logo após o restart — então este caso é diferente (mas pode se compor com) os leaks de pool já reportados em #106, #109, #112, #118, #165 e #175.
Logs da ocorrência de 27/08 (BRT)
11:11:03.029 [Client/Socket ERROR] Error reading from websocket: failed to get reader: failed to read frame header: EOF
11:22:38-41 [Client ERROR] Failed to sync app state after notification: ... mismatching LTHash (x7)
11:25:24.778 [Client ERROR] Event handler panicked while handling a *events.Archive: interface conversion: interface {} is *events.Archive, not map[string]interface {} (x2)
12:05:11.213 [Client ERROR] Unknown stream error: <stream:error><ack class="status" id="3EB0CEF3CE75AA87C361A8" type="media"/></stream:error>
12:05:11.395 [Client/Socket ERROR] Error reading from websocket: failed to get reader: failed to read frame header: EOF
--- (a partir daqui o cliente nunca mais volta; connect no-op, forcereconnect 500) ---
Na mesma tarde, outra instância caiu com a MESMA assinatura e outro id:
13:13:29.933 [Client ERROR] Unknown stream error: <stream:error><ack class="status" id="3EB049A3E64AAE7174E188" type="media"/></stream:error>
13:13:30.110 [Client/Socket ERROR] Error reading from websocket: ... EOF
Dois ids diferentes em quedas diferentes → não é uma stanza única "envenenada" da conta; qualquer recebimento/ack de status com mídia parece poder disparar. Nota: as instâncias são criadas com ignoreStatus=true — pode haver interação entre o descarte do status e o ack enviado.
O que precisa ser feito na próxima atualização (passo a passo)
-
Auto-reconexão após
stream:errordesconhecido. Hoje o cliente entra em estado terminal mas continua registrado como "Reconnecting". Um stream:error desconhecido deveria ser tratado como desconexão recuperável: teardown completo do cliente + reconexão com backoff exponencial (a credencial continua válida — provado pelo relogin instantâneo pós-restart). -
/instance/connecte/instance/forcereconnectprecisam destruir e recriar o cliente quando ele existe mas está morto. Hoje o connect cai no caminho "Instance already running, settings updated without restarting client" mesmo com o cliente morto, e o forcereconnect falha sem executar um teardown real. Sugestão: seConnected=false, sempre descartar o objeto atual e criar cliente novo do store. -
Logar o erro real do
Connect()no forcereconnect. O 500failed to connectem ~2s não emite NENHUMA linha de log — impossível diagnosticar em produção. O erro retornado pelo whatsmeow (ou o estado interno que impede a criação) precisa ir para o log. -
Investigar o ack de status/mídia rejeitado pelo servidor. O
<ack class="status" type="media"/>dentro do stream:error indica que o servidor considerou inválido um ack nosso referente a mensagem de status com mídia. Revisar o caminho de recebimento destatus@broadcastcom mídia quandoignoreStatus=true(ack duplicado? tipo errado? ack de algo que não deveria ser acked?). -
Corrigir o panic do handler de
*events.Archive(já reportado em panic: "interface conversion: interface {} is *events.Archive, not map[string]interface {}" can crash the process (v0.7.1) #95 e Bug: API /chat/archive - interface conversion: interface {} is *events.Archive, not map[string]interface {} (v0.7.2) #101) — no nosso ambiente ele aparece em rajadas nos mesmos períodos das quedas. O emissor de webhook faz type assertion paramap[string]interface{}e recebe o struct tipado. -
Observabilidade por instância: expor o último erro de desconexão/tentativa (
last_disconnect_error, timestamp da última tentativa de reconnect) no/instance/statusou/instance/info, para monitoramento externo detectar esse estado sem depender de log scraping.
Workaround que estamos usando
Timer systemd a cada 2 min que: (a) religa via POST /instance/connect qualquer instância connected=false com device válido no store; (b) se a mesma instância seguir caída por 3 ciclos (connect inerte = este bug), reinicia o serviço inteiro com cooldown de 30 min. Funciona, mas reiniciar o processo derruba momentaneamente as demais ~14 instâncias — por isso o fix no servidor é importante.
Obrigado pelo projeto! Posso fornecer mais logs, testar builds com o fix, ou detalhar o ambiente.
Activity
LineckerN commentedon Sep 8, 2026
Reproduzi a mesma assinatura em 0.7.2 e abri #190 com o que encontrei.
Causa: o
go.modfixawhatsmeow v0.0.0-20260630180629-b572e5bcb92b, de 30/06.Três correções entraram depois desse pin e batem com os sintomas:
28bfe530fadda74fa3462Um detalhe que pode explicar o
forcereconnectdevolvendo 500 no seu caso: aquio loop de reconexão vazava uma conexão PostgreSQL por ciclo. Depois de ~99
ciclos o
max_connections(100) esgotou com conexões ociosas de containers quejá não existiam, e o cliente passou a não conseguir mais nem iniciar:
Nesse ponto qualquer tentativa de reconectar falha, porque não há conexão
disponível para abrir o store. Se o seu banco for compartilhado com outra coisa,
vale checar
select count(*), client_addr from pg_stat_activity group by 2antesde reiniciar o processo — o restart mascara o sintoma, mas as conexões órfãs de
containers antigos podem continuar lá.
Depois de limpar as órfãs e subir o build do #190: 0 stream errors, 0 disconnects,
e uma fila offline de 3301 mensagens acumulada em 1,5 dia drenou por completo em
59 minutos. São ~1 h de produção, então é evidência forte, não prova.
deps: bump whatsmeow to 20260929 (Go 1.26) — stream-error reconnect, …
deps: bump whatsmeow to 20260904 (28bfe53) to fix stream-error reconn…
fix(whatsmeow): make the reconnect supervisor know when to stop and w…