Debug a failed task
Do this when a task ended infailed or blocked and you need to know why.
Work top-down: the failure code narrows the search to one class of problem, the
rollup tells you where in the run it happened, and the event log gives you the
exact call.
Do not start from the event log. A long run has hundreds of events and the
failure code eliminates most of them in one request.
Prerequisites
- The task id and its agent id.
- An API key for the owning workspace.
Steps
1. Read the failure code
error is the human-readable message. failure_reason is the stable code, and
it is what you route on:
A
blocked status with capability_unavailable is not a model failure — it is
the platform refusing to certify success it could not check.
2. Narrow with the rollup
tools_failedhigh relative totools_called— the agent is fighting its tools. Go to the tool events.llm_calls_failedabove zero — a provider problem. Go to the LLM events.iterationsequal to the cap withtools_failedat zero — the agent is looping without progress. Read the conversation.last_erroris usually the single most useful field on the page.
3. Find the failing event
Filter the durable log rather than reading all of it:
The full history is paginated;
has_next tells you when to advance page.
4. Check what the agent produced
A failed task can still have written files. They are often the fastest way to see what it was doing:5. If the failure looks like a permission denial
Tool calls blocked by policy fail with a denial rather than an error from the tool. Read the policy the task actually ran under — the resolved snapshot, not a re-resolution:Verify
You have finished debugging when you can name the failure code and point at the event that produced it. Confirm the two agree:error on the task. If they disagree, the task row was
reconciled from the workflow after the event was written — trust the task row.
Troubleshooting
failure_reason is null on a failed task. The task failed before the
workflow could record a reason, most often a dispatch failure. Check
execution_id: null means no workflow ever started, and error will carry the
submission error instead.
404 from /summary on a task that GET .../tasks/{task_id} returns. The
rollup is backed by the task_summary view, which is keyed on task, workspace,
and agent together. A task fetched with the wrong agent_id in the path 404s
here even though the task exists.
The event log is empty but the task ran. Events are written by an activity
that records its own database failures without failing the batch, so a database
problem during the run leaves gaps. The task row and the rollup are still
authoritative for the outcome.
A task shows blocked but the stream ended with task.failed. There is no
task.blocked event type. The feed reports task.failed for any unsuccessful
end; the row carries the more specific status and failure_reason.
A task is stuck at running and nothing is happening. There is no
wall-clock timeout on a task — the only stop conditions are goal achieved,
iteration limit, and budget. Check GET .../tasks/{task_id}/status for the live
workflow state, then cancel it if the workflow is gone.
Cost reads zero on a task that clearly called a model. total_cost is
written at terminal status. A task killed before finalization keeps the last
persisted value; use cost_usd from the rollup, which is derived from the event
log.