Diagnóstico
Todo fallo queda registrado. Empiece por la ejecución, no por sus logs.
# Todas las ejecuciones fallidas del último día
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"
# Todo sobre una de ellas
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 los pasos que fallaron, uno por línea, cada uno prefijado con su posición (step 2: …), así que la ejecución ya le dice dónde mirar antes de abrir los pasos.
"El correo se procesó pero no pasó nada en mi sistema"
Verifique, en este orden:
- ¿Hay ejecución?
GET /v1/workflow-runs?email_id=…. Si la lista viene vacía, el agente no devolvióworkflow— el análisis está enEmail.agent_metadata, y el problema es de prompt, no de integración. - ¿Hay una ejecución con
summary.total: 0y errorinvalid workflow …? El agente emitió algo que no validó. El valor problemático está endefinition. - Si no, la ejecución existe y su
errornombra el paso que falló.
Catálogo de errores
error | Qué pasó | Qué hacer |
|---|---|---|
invalid workflow at `0.url`: … | El array del agente no validó. No se envió nada. | Lea definition. Normalmente falta url, hay un type distinto de request, o una cabecera reservada. Corrija el prompt. |
workflow exceeds 20 steps | El agente produjo más pasos que el tope. | Divida el trabajo, o haga que el prompt condense en menos llamadas. |
secret `X` is not defined in this workspace | Una referencia ${{secrets.X}} no tiene secret correspondiente. No se envió nada. | GET /v1/secrets y compare. Error de tipeo en el prompt, secret eliminado, o el secret vive en otro workspace — nunca se comparten. |
\`steps.2.response.body.id\` refers to a step that has not run yet | Referencia hacia adelante — un paso que viene después. (Una referencia a un paso que falló o se saltó no hace fallar el paso: queda skipped.) | Los pasos solo ven pasos anteriores. Reordene, o corrija el índice. |
\`steps.0.response.body.data.id\` could not be resolved | La ruta no existe en esa respuesta. | Mire el response.body del paso 0 — la forma del destino probablemente no es la que supuso el prompt. |
\`steps.0.response.body.id\` resolved to null | La ruta existe pero es nula. | Igual que arriba: el destino no devolvió id, normalmente porque la llamada no hizo lo que usted cree. |
expected status 201, got 422 | El destino rechazó el payload. | El response.body de ese paso tiene el motivo. Problema de payload — corrija el prompt o el mapeo. |
expected status 200, got 401 | La credencial fue rechazada. | Rote con PUT /v1/secrets (misma key). Verifique que last_used_at avanzó — si no avanzó, el paso nunca llegó a usar ese secret. |
timeout after 30000ms | El destino no respondió en 30s. | Problema del destino. La petición pudo haberse procesado igual — trate la operación como desconocida, no como fallida. |
expected status 2xx, got 302 | El destino respondió una redirección. | Las redirecciones nunca se siguen — un 302 a un host privado saltaría todas las verificaciones — así que un 3xx es simplemente un status que no coincide. Apunte el paso a la URL final. |
url not allowed: private_ip | La URL resuelta apunta a una dirección privada, de loopback, link-local o de metadatos. | Esperado para hosts internos: los workflows solo alcanzan la internet pública. Exponga un endpoint, o use un webhook y llámese a sí mismo. |
url not allowed: http_not_allowed | http:// plano fuera de desarrollo. | Use https://. |
url not allowed: dns_failure | El hostname no resuelve. | Error de tipeo, o un nombre que solo resuelve dentro de su red. |
unsupported step type `delay` | Un paso guardado tiene un tipo que esta versión no ejecuta. | Hoy solo existe request. |
Leyendo la petición guardada
Cada paso conserva la petición tal como se envió — después de sustituir los placeholders, con los valores de secrets reemplazados 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"
}
Dos campos responden dos preguntas distintas:
requesten el paso — lo que realmente enviamos. Úselo contra problemas del lado del destino.definitionen la ejecución — lo que escribió el agente, con los placeholders sin resolver. Úselo contra problemas de prompt.
[REDACTED] no es una máscara sobre un valor que podríamos mostrarle: el texto original nunca se escribió. No intente recuperar una credencial desde una ejecución.
Status que no son fallos
| Situación | Significado |
|---|---|
Paso skipped | Referencia a un paso que no tuvo éxito (falló o se saltó) — el error dice depends on step N, which did not succeed. Nunca se envió. |
Ejecución running por mucho tiempo | La ejecución se interrumpió a mitad (reinicio de worker). No se reintenta, porque algunos pasos pueden haber llegado ya a sus sistemas. Reejecutar es una decisión humana — POST /v1/emails/{id}/reprocess reinicia el pipeline completo, incluida la llamada al agente. |
Ejecución pending | En cola, aún no iniciada. Normal durante unos segundos. |
summary.succeeded: 2 con status: failed | El paso 1 falló, el paso 2 dependía de él y se saltó, los pasos 0 y 3 son independientes y salieron bien. Esos dos sí ocurrieron en sus sistemas. |
Esa última fila es la que hay que interiorizar: una ejecución fallida no significa que no pasó nada. Los efectos parciales son reales y no hay rollback — los pasos independientes corren aunque otro falle, y un paso solo depende de lo que referencia. Diseñe la cadena de forma que cada paso sea aceptable de ocurrir sin los otros que no referencia (crear, luego adjuntar, luego notificar — no notificar, luego crear).
Vigilarlo de forma continua
Suscriba un webhook a workflow.run.failed en vez de consultar en bucle:
{
"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": "…" }
}
}
Las instantáneas de email y agent_run viajan con el evento, así una alerta puede llevar el asunto y el modelo sin una llamada extra.