Documentação / Falhas no Envio de Mensagens (Situações e Causas)

Falhas no Envio de Mensagens (Situações e Causas)

Entrar

Falhas no Envio de Mensagens (Situações e Causas)

Documento técnico: quais são as situações em que o envio de uma mensagem pode falhar na Pilot Status, considerando validações de API, enfileiramento, processamento no worker e feedback assíncrono via webhooks.

Este documento complementa a análise detalhada do fluxo em analise-fluxo-envio-mensagens.md.

1) Antes de enfileirar (falhas síncronas na API)

Estas falhas acontecem durante a chamada HTTP (a mensagem não chega a ser enfileirada no BullMQ).

1.1 Autenticação e autorização

1.2 Validação de payload e parâmetros

1.3 Regras de TEST (destino permitido)

1.4 Template (não encontrado / não aprovado / incompatível)

1.5 Instância WhatsApp (origem) e configuração

1.6 Restrições por categoria de template

1.7 Opt-in (transacional)

Observações:

1.8 Rate limit / assinatura / pacotes

1.9 Falhas internas (exceções)

  • Erros não mapeados (ex.: DB indisponível, bug, exceções de serviços internos) → 500

2) Após enfileirar (falhas assíncronas no worker)

Nestes casos a API normalmente responde sucesso (a mensagem foi criada com status=QUEUED), mas o envio pode falhar depois.

2.1 Lock / idempotência / duplicidade

  • Worker não adquire lock Redis (lock:ps:message:<id>) → job é ignorado sem erro (a mensagem permanece como está; tipicamente QUEUED)
  • Mensagem não existe no banco → job “morre” sem envio (log e retorno)
  • Mensagem não está QUEUED → worker não envia (idempotência por estado)

2.2 Expiração (deliverUntil)

  • Quando now > deliverUntil → marca FAILED e emite message.failed (se houver webhook do cliente)

2.3 Dados insuficientes ou inconsistentes para enviar

Situações comuns:

  • Template/body ausente ou inconsistência em modo bodyOverride.
  • Instância WhatsApp não determinada (nem na mensagem/job, nem via EVOLUTION_INSTANCE_NAME).
  • Campos mínimos necessários ausentes no registro.

Trechos:

  • Validações/renderização que podem resultar em FAILED: send-message.ts
  • Mensagens “órfãs”/inválidas também podem ser marcadas como FAILED pelo reconciliador: message-reconciler.ts

2.4 Falhas ao chamar a Evolution API (provider WhatsApp)

Mesmo com template válido, o envio pode falhar na integração com a Evolution:

  • Instância removida (erro “NotFound”) → marca FAILED, tenta marcar instância como CLOSE e dispara message.failed (sem retry do BullMQ)
  • Instância desconectada / conectando:
    • Com tentativas restantes → lança erro para o BullMQ retentar (backoff fixo 30s, até 10 tentativas)
    • Sem tentativas restantes → mantém QUEUED para o reconciliador tentar mais tarde
    • send-message.ts
  • Erro genérico na última tentativa → marca FAILED, dispara message.failed e finaliza como job falho (sem novas tentativas)

Configuração da integração (base URL/API key) está centralizada em whatsapp.ts.

2.5 Reconciliação (mensagem fica QUEUED tempo demais)

O reconciliador é uma “rede de segurança” para mensagens que ficaram presas em QUEUED por inconsistência entre DB e fila:

  • Mensagem expirada → FAILED e remoção de job (se existir)
  • Mensagem QUEUED há tempo demais (timeout padrão 7 dias) → FAILED
  • Job sumiu da fila → reenfileira
  • Job está failed/completed mas DB ainda QUEUED → reenfileira ou corrige

Referência: message-reconciler.ts

3) “Falha de envio” vs “falha de visibilidade de status” (webhooks)

Existe um caso comum onde o envio pode até ter ocorrido, mas o sistema não consegue confirmar/atualizar o status corretamente.

3.1 Status SENT/DELIVERED/READ depende de webhook assíncrono

Após a chamada bem-sucedida para a Evolution, o worker grava evolutionKeyId e evolutionInstanceId, mas o status SENT “oficial” depende do webhook messages.update (ack assíncrono).

  • Persistência pós-chamada (sem marcar SENT): send-message.ts
  • Handler de atualização de status (ack → SENT/DELIVERED/READ; erro → FAILED): messages-update.ts

Consequências:

  • Se o webhook não chegar, a mensagem pode ficar como QUEUED no banco mesmo tendo sido enviada.
  • O reconciliador pode reenfileirar mais tarde, aumentando risco de duplicata.

3.2 Falhas na ingestão do webhook da Evolution (RabbitMQ → webhook interno)

Quando a ingestão está via RabbitMQ consumer, falhas ao encaminhar o evento da Evolution para o webhook interno disparam retries em filas .retry.* e, após exceder, enviam para .dlq.

Isso pode causar “atraso” ou “perda” de acks de status (ex.: SERVER_ACK), afetando a visibilidade do status no DB e os webhooks do cliente.

4) Como a falha aparece para o cliente

4.1 Na resposta HTTP (falhas síncronas)

  • O request falha com 4xx/5xx e o cliente recebe o motivo (às vezes com um code específico, dependendo da rota).
  • A mensagem normalmente não é criada nem enfileirada quando a falha é de validação/auth/rate-limit antes do MessageService.send().

4.2 Como status de mensagem (falhas assíncronas)

  • message.status pode ir para FAILED com errorMessage (ex.: expirou, dados inválidos, erro na Evolution).
  • A transição para SENT/DELIVERED/READ é baseada no webhook messages.update.

4.3 Via webhook outbound do cliente (message.failed)

  • Quando o sistema marca uma mensagem como FAILED, ele tenta notificar o cliente via webhook configurado.
  • Em caso de indisponibilidade do endpoint do cliente, o envio do webhook é “best-effort” (sem retry dedicado).

5) Checklist rápido de diagnóstico

  1. O envio falhou na hora (API retornou 4xx/5xx)?
  2. A mensagem existe no banco e está QUEUED há muito tempo?
  3. deliverUntil já expirou (especialmente OTP)?
  4. A instância está OPEN no banco e disponível na Evolution?
  5. evolutionKeyId preenchido, mas status ainda QUEUED (suspeita de falha/atraso no webhook messages.update)?
  6. Em ingestão via RabbitMQ, há eventos acumulando em .retry.*/.dlq?