The two budgets
max_steps counts rounds, not tool calls. Four tools called in one reply cost one step. It is the runaway-loop guard — the number that stops a model looping over a search tool forever — not the time budget.
timeout_seconds is the real bound. A run that ends without a result is re-prompted up to twice and each re-prompt starts the step budget over, so a task with max_steps: 25 can legitimately spend 75 rounds before anything gives up. Only the clock stops that. See running and runs.
Lowering max_steps to tidy up is the most common way to break a research task: it fails with step_limit, which reads like the prompt is wrong when it is not.
error.type
A failed run carries error: {type, message}. type is a closed vocabulary of seven, and the point of it is that each one sends you somewhere different.
A failed run is still a
200 with status: "failed" — the run happened, and its trace, tokens and cost are on the response. The 4xx cases below are the ones where no run happens at all.
Refused when you run
POST /v1/tasks/{task_id}/runs checks the input contract before it starts anything, and returns a 400 rather than recording a failed run.
The
400 bodies use the standard envelope, with every problem listed at once:
Refused when you save
There is no publish step, so every check runs at save, including the ones an agent draft is allowed to defer. An invalid config is a400 listing every problem, and nothing is stored — which is what makes “a task that exists can always run” true.
DELETE /v1/tasks/{task_id} is refused with a 409 while an email batch is still drafting with that task, naming the batches. Cancel them first. Deleting a task that finished drafting a batch is fine — the batch keeps rendering, and so does the run history.
Warnings
create_task and update_task return warnings beside the saved task. These do not block the save, and there is no publish screen to read them on, so read them where they come back.