Diagnóstico
Toda falha fica registrada. Comece pela execução, não pelos seus logs.
# Todas as execuções falhas do último dia
curl "https://api.inbox.kobana.com.br/v1/workflow-runs?status=failed&created_at[gte]=2026-09-08T00:00:00Z" \
--cert ./client.crt --key ./client.key -H "Authorization: Bearer $JWT"
# Tudo sobre uma delas
curl "https://api.inbox.kobana.com.br/v1/workflow-runs/$RUN_ID" \
--cert ./client.crt --key ./client.key -H "Authorization: Bearer $JWT"
WorkflowRun.error lista todos os passos que falharam, um por linha, cada um prefixado com sua posição (step 2: …), então a execução já diz onde olhar antes de você abrir os passos.
"O e-mail foi processado mas nada aconteceu no meu sistema"
Verifique, nesta ordem:
- Existe execução?
GET /v1/workflow-runs?email_id=…. Se a lista vier vazia, o agente não devolveuworkflow— a análise está emEmail.agent_metadata, e o problema é de prompt, não de integração. - Existe execução com
summary.total: 0e erroinvalid workflow …? O agente emitiu algo que não validou. O valor problemático está emdefinition. - Caso contrário, a execução existe e o
errordela nomeia o passo que falhou.
Catálogo de erros
error | O que aconteceu | O que fazer |
|---|---|---|
invalid workflow at `0.url`: … | O array do agente não validou. Nada foi enviado. | Leia definition. Normalmente é url faltando, um type diferente de request, ou um header reservado. Corrija o prompt. |
workflow exceeds 20 steps | O agente produziu mais passos que o teto. | Divida o trabalho, ou faça o prompt condensar em menos chamadas. |
secret `X` is not defined in this workspace | Uma referência ${{secrets.X}} não tem secret correspondente. Nada foi enviado. | GET /v1/secrets e compare. Erro de digitação no prompt, secret excluído, ou o secret está em outro workspace — eles nunca são compartilhados. |
\`steps.2.response.body.id\` refers to a step that has not run yet | Referência para frente — um passo que vem depois. (Referência a um passo que falhou ou foi pulado não falha o passo: ele fica skipped.) | Passos só enxergam passos anteriores. Reordene, ou corrija o índice. |
\`steps.0.response.body.data.id\` could not be resolved | O caminho não existe naquela resposta. | Olhe o response.body do passo 0 — o formato do destino provavelmente não é o que o prompt supôs. |
\`steps.0.response.body.id\` resolved to null | O caminho existe mas é nulo. | Mesmo caso acima: o destino não devolveu id, normalmente porque a chamada não fez o que você imagina. |
expected status 201, got 422 | O destino recusou o payload. | O response.body daquele passo tem o motivo. Problema de payload — corrija o prompt ou o mapeamento. |
expected status 200, got 401 | A credencial foi recusada. | Rotacione com PUT /v1/secrets (mesma key). Confira se last_used_at avançou — se não avançou, o passo nunca chegou a usar aquele secret. |
timeout after 30000ms | O destino não respondeu em 30s. | Problema do destino. A requisição pode ter sido processada mesmo assim — trate a operação como desconhecida, não como falha. |
expected status 2xx, got 302 | O destino respondeu um redirecionamento. | Redirects nunca são seguidos — um 302 para um host privado furaria todos os checks — então um 3xx é simplesmente um status que não bate. Aponte o passo para a URL final. |
url not allowed: private_ip | A URL resolvida aponta para endereço privado, loopback, link-local ou de metadados. | Esperado para hosts internos: workflows só alcançam a internet pública. Exponha um endpoint, ou use um webhook e chame você mesmo de volta. |
url not allowed: http_not_allowed | http:// puro fora de desenvolvimento. | Use https://. |
url not allowed: dns_failure | O hostname não resolve. | Erro de digitação, ou um nome que só resolve dentro da sua rede. |
unsupported step type `delay` | Um passo guardado tem um tipo que esta versão não executa. | Só existe request hoje. |
Lendo a requisição guardada
Cada passo guarda a requisição como enviada — depois da substituição de placeholders, com valores de secret trocados por [REDACTED]:
{
"position": 0,
"status": "failed",
"request": {
"url": "https://erp.example.com/payables",
"headers": { "Authorization": "Bearer [REDACTED]", "content-type": "application/json" },
"body": "{\"supplier\":\"ACME\",\"amount\":1250.5}"
},
"response": {
"status": 422,
"expected_status": 201,
"body": "{\"error\":\"supplier not found\"}"
},
"error": "expected status 201, got 422"
}
Dois campos respondem a duas perguntas diferentes:
requestno passo — o que de fato enviamos. Use contra problemas do lado do destino.definitionna execução — o que o agente escreveu, com placeholders sem resolver. Use contra problemas de prompt.
[REDACTED] não é uma máscara sobre um valor que poderíamos mostrar: o texto original nunca foi gravado. Não tente recuperar uma credencial a partir de uma execução.
Status que não são falha
| Situação | Significado |
|---|---|
Passo skipped | Ele referencia um passo que não teve sucesso (falhou ou foi pulado) — o error diz depends on step N, which did not succeed. Nunca foi enviado. |
Execução running por muito tempo | A execução foi interrompida no meio (restart de worker). Ela não é reexecutada, porque alguns passos podem já ter chegado aos seus sistemas. Reexecutar é decisão humana — POST /v1/emails/{id}/reprocess recomeça o pipeline inteiro, chamada ao agente inclusive. |
Execução pending | Na fila, ainda não iniciada. Normal por alguns segundos. |
summary.succeeded: 2 com status: failed | O passo 1 falhou, o passo 2 dependia dele e foi pulado, os passos 0 e 3 são independentes e deram certo. Esses dois aconteceram nos seus sistemas. |
Essa última linha é a que precisa ficar clara: uma execução falha não significa que nada aconteceu. Efeitos parciais são reais, e não existe rollback — passos independentes rodam mesmo quando outro falha, e um passo só depende do que ele referencia. Desenhe a cadeia de forma que cada passo seja aceitável de acontecer sem os outros que ele não referencia (criar, depois anexar, depois notificar — não notificar, depois criar).
Acompanhando continuamente
Assine um webhook em workflow.run.failed em vez de ficar consultando:
{
"event": { "slug": "workflow.run.failed" },
"data": {
"workflow_run_id": "9c8b7a65-…",
"summary": { "total": 3, "succeeded": 1, "error": 2 },
"failed_step": 1,
"failed_steps": [1],
"error": "expected status 201, got 422",
"email": { "id": "…", "subject": "…" },
"agent_run": { "id": "…", "llm_model": "…" }
}
}
Os snapshots de email e agent_run vêm junto, então um alerta pode carregar o assunto e o modelo sem uma chamada extra.