Skip to main content

Debug a failed task

Do this when a task ended in failed 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

Read it as a triage table:
  • tools_failed high relative to tools_called — the agent is fighting its tools. Go to the tool events.
  • llm_calls_failed above zero — a provider problem. Go to the LLM events.
  • iterations equal to the cap with tools_failed at zero — the agent is looping without progress. Read the conversation.
  • last_error is usually the single most useful field on the page.

3. Find the failing event

Filter the durable log rather than reading all of it:
Useful filters: 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:
This message matches 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.