Catálogo técnico de erros¶
O projeto distingue exceções Python, respostas HTTP e resultados de integração. Eles não são intercambiáveis: uma StubResponse(success=False) é um retorno controlado; um batchItemFailures instrui SQS a repetir; HANDOFF_PENDING entrega o caso a um humano. Comece pelo evento e pela camada em que a falha apareceu, não apenas pela mensagem final ao cliente.
Fontes: src/core/errors.py, src/core/application/process_message_use_case.py, src/core/application/confirm_action.py, src/orchestrator/app.py, src/webhook_handler/app.py, src/emulator/app.py e adapters em src/core/integrations/. Logs abaixo usam nomes reais; exemplos usam dados fictícios.
Exceções de domínio e aplicação¶
| Erro | Causa/sintoma | Log esperado ou ponto de observação | Retry? | Ação recomendada e escalonamento |
|---|---|---|---|---|
DomainError |
Invariante inválida, como alterar campo imutável de Session |
Exceção no chamador; pode aparecer em process_immediate_turn_failed |
SQS pode repetir se escapar | Corrigir consumidor; não forçar dados persistidos |
IllegalStateTransitionError |
Session.transition_to() recebe par fora de LEGAL_TRANSITIONS |
Campos from_state/to_state; force_handoff_blocked_by_state no fallback terminal |
Não é defeito de rede; repetição sem mudança tende a falhar | Conferir máquina de estados, origem da ação e concorrência |
InvalidAgentCodeError |
Código s não permitido para aquele agente |
llm_failure_handoff |
Sem retry semântico do código | Handoff llm_invalid_json; revisar prompt e tabela de códigos |
LLMResponseParseError |
Texto antes de JSON, chaves desbalanceadas, JSON malformado, s ausente/string/bool |
llm_response_parse_retry, llm_response_repair_success ou llm_response_repair_failed |
Uma chamada de reparo do formato | Se reparo falhar, handoff llm_invalid_json; guardar exemplo sanitizado para regressão |
ApplicationError em action mapping/invocador |
Enum/campo inválido ou agente sem builder/modelo | llm_failure_handoff quando capturada no loop |
Não repete automaticamente como rede | Motivo salvo llm_invalid_json; verificar contrato retornado |
ApplicationError de dispatch |
DEBOUNCE_QUEUE_URL ausente |
Falha do webhook no dispatch | Resolver configuração antes de repetir | Conferir env e recurso SAM; não houve processamento downstream garantido |
MissingRequiredFieldError |
Classe declarada para ausência de campo de serviço | Sem uso/raise encontrado em src/ atual |
Não aplicável como fluxo ativo | Consultar validação real em action mapping/catálogo; não atribuir esse erro a todo campo ausente |
ContextPackageNotFoundError |
Classe declarada para package inexistente | Sem uso/raise encontrado em src/ atual |
Não aplicável como fluxo ativo | KnowledgeBaseRepository.get_package() retorna None; consultar fluxo de contexto atual |
_TurnSupersededError |
Turno perdeu atualidade/posse antes de efeito final | Resultado superseded, eventos do orchestrator |
Normalmente conclui receipt antigo; novo turno prossegue | Comportamento esperado de latest-message-wins; não é handoff por erro |
Docstrings não substituem o fluxo atual
LLMResponseParseError descreve handoff no comentário da classe, mas o fluxo atual tenta reparar o formato primeiro. Duas classes de erros de campos/contexto continuam declaradas sem uso no runtime. O fallback _handle_llm_failure() classifica toda IntegrationError como llm_timeout, e outros erros capturados de aplicação/contrato como llm_invalid_json; o nome do motivo não é diagnóstico suficiente.
Exemplos mínimos do parser:
{"s":1}Resposta válida.
Olá! {"s":1}Resposta inválida: texto antes do JSON.
{"s":"1"}Resposta inválida: código é string.
{"s":true}Resposta inválida: booleano não é código inteiro.
Whitespace antes do objeto é permitido. O parser reconhece aspas e escapes ao contar chaves. O reparo não autoriza o agente a mudar fatos ou executar ações fora do contrato; veja resposta dos agentes.
OpenAI e geração de resumo¶
| Erro | Causa/sintoma | Log | Retry? | Ação e handoff |
|---|---|---|---|---|
LLMTimeoutError |
APITimeoutError esgota loop do wrapper |
llm_timeout → llm_failure_handoff |
1 + LLM_RETRY_MAX chamadas do wrapper; SDK também pode repetir |
Verificar tempo total/modelo; handoff llm_timeout |
LLMProviderError, 401/403 |
Chave ou permissão inválida | llm_4xx com status/modelo |
Wrapper falha imediatamente | Corrigir chave/permissão; motivo registrado também llm_timeout |
LLMProviderError, 400 |
Modelo/reasoning/request incompatível | llm_4xx |
Sem retry do wrapper | Conferir settings resolvidas e payload Responses |
LLMProviderError, 429 |
Limite/cota do provider | llm_4xx |
Sem retry do wrapper | Conferir limite na conta; não aumentar retry indiscriminadamente |
LLMProviderError, 5xx esgotado |
Instabilidade do serviço | llm_5xx → llm_failure_handoff |
Loop do wrapper | Investigar recorrência; handoff llm_timeout |
| Exceção SDK fora das capturadas | Por exemplo conexão não convertida em timeout/status | Pode chegar a process_immediate_turn_failed |
Handler pede retry SQS | Investigar classe real; não assumir que todo erro vira IntegrationError |
Resumo com IntegrationError |
Provider falhou ao produzir resumo | summary_llm_failed |
Provider aplica sua política; resumo usa fallback | Continua com objeto mínimo e histórico |
| Resumo não parseável | Saída não é objeto JSON válido | summary_parse_failed |
Sem reparo adicional do resumo | Continua com fallback; revisar prompt/resultado |
OpenAIClient não configura max_retries do SDK. Tokens/latência de LLMCallResult correspondem à tentativa final bem-sucedida; não use esses valores isolados para estimar todas as tentativas. Detalhes em OpenAI.
Webhook e entrega WhatsApp¶
| Erro/status | Causa/sintoma | Log | Retry? | Ação e handoff |
|---|---|---|---|---|
GET 403 verification_failed |
Token/mode de verificação incorreto | webhook_received |
Repetir só após corrigir configuração Meta/env | Conferir WHATSAPP_VERIFY_TOKEN e challenge; não há sessão |
POST 400 bad_request |
Body ausente/JSON inválido/campos ausentes | bad_input |
Reenviar payload corrigido | Conferir contrato e bytes/base64; sem handoff de cliente válido |
POST 401 invalid_signature |
HMAC ausente/incorreto | whatsapp_signature_invalid |
Não repetir mesmo corpo sem corrigir assinatura | Conferir app secret e bytes originais; não desabilitar proteção em produção |
POST 403 forbidden |
Formato interno phone_number em Cloud mode |
internal_payload_rejected_in_cloud_mode |
Não | Usar webhook Meta assinado; formato interno é de emulação |
POST 200 ignored |
Status-only callback | webhook_received |
Não necessário | Sucesso sem mensagem processável, não defeito |
whatsapp_http_400/401/403 |
Request/credencial/permissão/janela rejeitada | whatsapp_cloud_send_http_failed |
Sem retry HTTP local; orchestrator pode reentregar via SQS | Corrigir causa antes de reprocessar; não altera automaticamente para handoff |
whatsapp_http_429/5xx |
Limite/indisponibilidade | whatsapp_cloud_send_http_failed ao esgotar |
Até 2 retries locais, depois SQS no orchestrator | Conferir fila e provider |
OSError/URLError/JSON inválido |
Transporte ou response inesperada | whatsapp_cloud_send_failed |
Client não repete; orchestrator repete item | A resposta persistida pode ser reenviada; aceitação remota incerta exige cuidado com duplicidade |
ValueError destino inválido |
Telefone sem dígitos | Exceção antes do loop de HTTP | Não resolve sem corrigir destino | Revisar normalização/origem |
| Falha de envio síncrono | Resposta direta do webhook não entregue | sync_whatsapp_reply_failed |
Não tem o mesmo fluxo de reentrega do orchestrator | Conferir recibo/caminho específico, credenciais e janela |
success=True sem ID remoto é possível no client WhatsApp; callbacks de status não são confirmação durável de entrega neste código. Veja integração WhatsApp.
DynamoDB, concorrência e SQS¶
| Erro/sinal | Causa/sintoma | Log/observação | Retry? | Ação e escalonamento |
|---|---|---|---|---|
DynamoConditionFailedError |
Estado/sessão/lock mudou ou não existe | Tratamento varia conforme consumidor | Relê em operações humanas; CAS interno em projeção | Reconsultar estado/posse; não remover ConditionExpression |
ConditionalCheckFailedException no CAS CRM |
etag concorrente |
Repository relê até 8 vezes | Sim, limitado | Se esgotar: crm_sync_concurrent_update; investigar contenção |
Could not update operator availability under contention |
20 tentativas CAS de disponibilidade falharam | Exceção na ação de operador | Não faz fallback inseguro | Repetir comando após reduzir contenção; preservar mapa de outros operadores |
bad_sqs_body |
Corpo sem telefone/JSON inválido | Evento com messageId |
Handler não reencaminha: considera irrecuperável | Corrigir produtor e investigar mensagem; não esperar DLQ desse ramo |
process_immediate_turn_failed / process_message_failed |
Exceção não tratada no processamento | Stack/erro e correlação | batchItemFailures pede retry do item |
Verificar causa antes de redrive; pode ir a DLQ após política SQS |
whatsapp_send_failed |
Processamento persistiu, entrega falhou | Evento de saída + item em falha | Reutiliza texto persistido para reentrega | Não repetir inferência manualmente; conferir provider e sessão ativa |
buffer_already_flushed |
Evento legado sem buffer válido | INFO | Não | Evento esperado de TTL/concorrência, sem trabalho a fazer |
| SDK DynamoDB indisponível/permissão negada | Endpoint/tabela/role errados | Exceção SDK no chamador/handler | Depende do SDK e caminho | Confirmar ambiente/tabela, IAM e endpoint local; não zerar tabela |
O comentário de DynamoConditionFailedError menciona HTTP 409, mas não existe mapeamento universal para 409. No painel humano, conflitos são traduzidos em erro seguro e redirect 303; no SQS não há status HTTP ao usuário. Consulte DynamoDB e SQS para condições, visibilidade e DLQ.
CRM e projeção comercial¶
| Erro | Causa/sintoma | Log/registro | Retry? | Ação e handoff |
|---|---|---|---|---|
hubspot_missing_contact_identifier |
Sem telefone/email | Evento com mesmo nome | Não resolve repetindo | Corrigir dados; confirmação pode virar api_failure |
hubspot_http_401/403 |
Token/permissão | hubspot_http_failed |
Não no client | BLOCKED no worker; corrigir credencial/permissão |
hubspot_http_404 |
Rota ou ID inexistente | hubspot_http_failed |
Não cria substituto | Worker BLOCKED; validar base e ID; não interpretar busca como vazia |
hubspot_http_409 |
Conflito de unicidade | hubspot_http_failed |
Contato pesquisa outra vez; deal não é upsert | Contato pode recuperar por PATCH; deal bloqueia |
hubspot_http_429 |
Limite | hubspot_http_failed após retries aplicáveis |
Worker PENDING, backoff durável |
Aguardar próxima execução e observar backlog |
hubspot_http_5xx / timeout/rede em PATCH |
Falha transitória | hubspot_http_failed/hubspot_request_failed |
PENDING |
Retry de 1 até 60 minutos |
| Erro de POST de deal sem prova de rejeição | Pode ter criado remotamente | UNCERTAIN, deal_sync_failed |
Não repete criação | Confirmar no CRM antes de intervenção |
hubspot_missing_deal_id após POST |
Resposta sem ID | UNCERTAIN |
Não | Investigar resultado remoto |
hubspot_invalid_response |
JSON inválido | Evento com mesmo nome | No POST torna criação incerta | Validar contrato; não reconstruir deal automaticamente |
stage_configuration_invalid |
Stage ausente/terminal igual ao ativo | CRM_SYNC.last_error, status BLOCKED |
Não automático | Configurar stages explícitos e recuperar projeção controladamente |
creation_result_unknown |
Marca de criação persistida sem ID após lease | CRM_SYNC.last_error, UNCERTAIN |
Não | Reconciliação externa obrigatória antes de liberar POST |
crm_deal_id_persist_failed |
CRM respondeu, mas ID não pôde ser salvo | crm_sync_worker_failed |
Lease/marca preservados | Conferir negócio remoto e vínculo; evitar duplicar |
Client comum tem retry HTTP, mas src/crm_sync/app.py fixa zero e delega retry à projeção durável. Falhas síncronas de contato na confirmação ainda podem gerar api_failure; falha posterior de projeção não desfaz atendimento fechado. O worker grava last_error genérico (contact_sync_failed, deal_sync_failed) e classifica o estado; procure logs do adapter para o HTTP específico. Veja CRM e sincronização.
eGestor e confirmação cadastral¶
| Erro | Causa/sintoma | Log | Retry? | Ação e handoff |
|---|---|---|---|---|
egestor_missing_document |
Ausente/tamanho diferente de 11/14 dígitos | Mesmo nome | Não | Corrigir documento; sem HTTP |
egestor_lookup_invalid_response |
Paginação/tipos/total/candidato inválidos | egestor_customer_lookup_blocked |
Não escreve | Conferir contrato; api_failure na confirmação |
egestor_lookup_unsafe |
Candidatos sem documento exato | egestor_customer_lookup_blocked |
Não escreve | Investigar filtro; nunca aceitar primeiro resultado divergente |
egestor_lookup_ambiguous |
Mais de um código para documento exato | egestor_customer_lookup_blocked |
Não escreve | Resolver duplicidade com equipe ERP |
egestor_lookup_pagination_limit |
Mais de dez páginas | egestor_customer_lookup_blocked |
Não escreve | Conferir filtro e volume |
egestor_http_401/403 |
Credencial/permissão | egestor_http_failed |
Sem retry local | Conferir token e troca OAuth; api_failure |
egestor_http_404 inicial de lookup |
Contrato considera ausência | Lookup resolvido para criação | Pode prosseguir a POST | Verificar base: 404 de rota incorreta também pode entrar nesse ramo |
egestor_http_409 no POST |
Documento concorrente | egestor_http_failed |
Refaz busca e atualiza somente exato | Se não resolver, falha controlada |
egestor_http_429/5xx ou rede |
Transitório | egestor_http_failed/egestor_request_failed |
Até 2 retries default por request | Esvaziamento leva a api_failure; sem rollback cross-provider |
egestor_oauth_exchange_skipped |
Troca OAuth falhou | DEBUG | Usa token original; sem loop próprio OAuth | Conferir tipo de token, endpoint e resposta |
egestor_lookup_metric_failed |
CloudWatch negado/indisponível | Mesmo nome | Não libera lookup | Corrigir observabilidade; mantém bloqueio de escrita |
| Campo inválido na confirmação | Nome/email/CPF/CNPJ/CEP/UF/telefone/endereço/cidade/descrição | Resposta fixa de correção | Incrementa retry_count; não chama integrações |
Terceira falha gera confirm_retries_exhausted, handoff incerteza |
Envelope vazio {"current_page":1,"last_page":0,"total":0,"data":[]} é aceito atualmente. Afirmar que toda resposta last_page=0 é erro repetiria uma regra já corrigida. Veja eGestor.
Handoff, autenticação e operação humana¶
| Erro/sinal | Causa/sintoma | Log/HTTP | Retry? | Ação |
|---|---|---|---|---|
queued ou stub não notificado |
Sem operador disponível ou simulação | handoff_queued / stub_handoff_notify |
Não é falha técnica | Verificar tipo, whitelist e opt-in do dia |
| Todos os operadores falharam | HandoffNotifyResult.detail=send_failed |
handoff_operator_notified com sent=false, handoff_notify_failed |
Política WhatsApp por destino | Sessão continua pendente; conferir canal |
handoff_email_failed:<classe> |
SES falhou | handoff_email_failed |
Retry em transitórios | Conferir role, identidade SES, região e destinatários |
| Notificador lançou exceção | Dependência inesperada | handoff_notify_exception |
Orquestração mantém fila | Investigar provider sem desfazer sessão |
| Renotificação fora de janela | Último inbound não é recente | handoff_renotify_outside_window |
Pode reconsiderar em próximo ciclo | Não forçar mensagem ao cliente; equipe já pode ter sido notificada |
HumanConversationError |
Texto vazio ou acima de 4096 caracteres | Redirect 303 com erro seguro no painel | Após corrigir entrada | Não requer handoff adicional |
ConversationNotFoundError |
Sessão ausente | Erro seguro na tela | Reconsultar | Conferir telefone/sessão ativa |
ConversationStateError |
Sessão saiu do estado esperado | Redirect 303 | Atualizar página | Conferir ação concorrente/encerramento |
ConversationOwnershipError |
Classe declarada, sem uso atual | Não há fluxo ativo de exclusividade por owner | Não aplicável | Inbox é colaborativa entre operadores autenticados |
CustomerWindowClosedError |
Sem inbound nos últimos 24 h | Erro seguro na tela | Só com nova mensagem do cliente | Não há templates nesse fluxo |
HumanMessageSendError por lock |
Envio simultâneo com lock de 90 s | Mensagem “outra mensagem está sendo enviada” | Aguardar | Não enviar em paralelo |
HumanMessageSendError por provider |
WhatsApp retornou falha | Erro seguro na tela | Tentativa deliberada após diagnóstico | Atendimento permanece em andamento |
| Histórico falhou após envio | Mensagem já foi enviada | human_message_transcript_persist_failed; transcript_persisted=false |
Não reenviar | Confirmar registro com suporte; mensagem da tela orienta não duplicar |
| Liberação de lock falhou | Lock não removido após ação | human_send_lock_release_failed |
Expira por lease | Não converter sucesso de envio em retry do navegador |
| Sessão de operador expirada | Cookie/token inválido ou fora do dia | 401 operator session expired |
Novo login | Usar entrar do telefone autorizado |
| CSRF inválido | Token de formulário incorreto | 403 invalid csrf token |
Recarregar/login | Não desabilitar validação |
| Admin sem chave configurada | ADMIN_API_KEY ausente |
503 ADMIN_API_KEY is not configured |
Após configurar | Não aceitar chave em URL |
| Admin com chave errada | Header ausente/incorreto | 401 invalid admin api key |
Após corrigir autenticação | Usar X-Admin-API-Key, sem expor valor |
Diagnóstico e testes¶
Rastreie uma ocorrência por request_id do handler, provider_message_id do receipt e session_id. No CRM, associe o item CRM_SYNC#<session_id> e o ID externo. Preserve uma cópia sanitizada do erro, camada, método, status, horários e decisão de retry; não cole tokens, documentos ou conversa real em tickets públicos. O formatter JSON mascara padrões/chaves conhecidas, mas exc_info não passa pelo mesmo scrub, e chaves externas novas podem escapar.
Testes de referência: tests/unit/application/test_parser.py, test_process_message_use_case.py, test_confirm_action.py, test_human_conversation.py, test_crm_sync.py, tests/unit/domain/test_states.py, test_codes.py, tests/unit/integrations/test_openai_client.py, test_hubspot_http_client.py, test_egestor_http_client.py, test_handoff_email_notifier.py, e tests/unit/shared/test_logging_scrubbing.py.
Não considere a existência de um teste prova de saúde externa atual. Para procedimentos de incidente use troubleshooting, observabilidade e smokes, mantendo a distinção entre leitura e operações reais.