Dani Reyes11 min read17 views

Your task budget caps a turn, not a task, and Continue. buys a fresh one

A task budget is not a per-task cost ceiling. It resets whenever you send a user message carrying no tool results, which is exactly what most harnesses do when Claude finishes gracefully.

Flat vector schematic on deep navy. A stepped lime bar descends left to right, then an upward lime arrow snaps it back to full at a vertical boundary marker. The pattern repeats three times, showing a token budget depleting across one agentic turn and resetting at the next.
Flat vector schematic on deep navy. A stepped lime bar descends left to right, then an upward lime arrow snaps it back to full at a vertical boundary marker. The pattern repeats three times, showing a token budget depleting across one agentic turn and resetting at the next.
On this page

Quick answer

September 2026. Anthropic's task_budget does not cap a task. It caps a turn, and your harness decides where turns end.

The rule is mechanical: a user message that carries no tool_result blocks starts a new turn with a fresh budget. A user message that carries tool_result blocks continues the current turn. That is the whole discriminator, and it is not about who is speaking or what the text says.

Which means the most natural thing to do when Claude stops because the budget ran out, sending a follow-up that says Continue., hands it a brand new full budget. Anthropic's own documentation uses exactly that case as its example. If your loop auto-continues on end_turn, a 64,000 token budget is a 64,000 token budget per attempt, not per task.

Second thing, and it is what made this hard to see: there is no remaining-budget field in the response. You configure a number that only the model can read.

Claude

The moment

I had a review loop pointed at a mid-sized repo, running on claude-opus-5 with a task budget of 64,000 tokens and max_tokens at 128,000. The budget was there to stop a long audit from wandering. I picked 64,000 because I had measured a representative run at roughly 50,000 and wanted headroom.

The loop worked. The output was good. The token spend was nothing like 64,000.

I went looking for the leak in the usual places first. I checked whether thinking tokens were counted (they are). I checked whether my tool results were unusually large (they were not). I checked whether I had fat-fingered the total. I spent a while suspecting the budget was simply being ignored, because the documentation is clear that it is advisory rather than enforced, and "advisory" is a comfortable thing to blame.

None of that was it. My harness retried on end_turn. Claude would pace itself against the countdown, wrap up gracefully exactly as designed, emit end_turn, and my code would cheerfully send a bare continuation message with no tool results in it. That is the documented signal for a new turn. Fresh budget, every time.

The budget was working perfectly. I had just misunderstood what it was counting, and the API gives you no field to check.

Finding 1: three different things in this API are called a budget

This is worth separating before anything else, because two of them are hard limits and one is not, and they are denominated in different units.

Scroll to see more

What it capsEnforced?UnitDoes the model see it?
max_tokensOne requestYes, hardOutput tokensNo
task_budgetOne agentic turnNo, advisoryTokens across the loopYes, as a countdown
Managed Agents session budgetOne sessionYes, hardMoneyIndirectly

max_tokens truncates with stop_reason: "max_tokens". task_budget is a pacing hint the model can exceed if interrupting an action mid-flight would be worse than finishing it. The Managed Agents session budget is a different surface entirely: it is a spend cap, it pauses the session with stop_reason: budget_reached, and a session paused that way will only accept settle events, meaning events that resolve work already in progress rather than starting new work.

The documentation is explicit that task_budget and max_tokens are orthogonal, and that one is not required to be at or below the other. They are measuring different spans. If you want a genuine cost ceiling on the Messages API, max_tokens is your enforcement and task_budget is your pacing.

Finding 2: a turn ends when you stop sending tool results

Here are the two message shapes that decide everything.

This starts a new turn and resets the budget:

json
{ "role": "user", "content": "Continue." }

This continues the current turn and keeps the countdown:

json
{
  "role": "user",
  "content": [
    { "type": "tool_result", "tool_use_id": "toolu_01", "content": "npm audit output" }
  ]
}

And this also continues the turn, which is the part I would have got wrong if I had guessed:

json
{
  "role": "user",
  "content": [
    { "type": "tool_result", "tool_use_id": "toolu_01", "content": "npm audit output" },
    { "type": "text", "text": "Also check the Dockerfile." }
  ]
}

Adding new instructions alongside a tool result does not start a new turn. The presence of the tool_result block is what matters, because your client is still resolving calls that belong to the turn already in progress.

One caveat that stops this being a clean reset, and it cuts the other way: earlier turns' history still counts against the new turn's countdown for as long as it remains in the context. So the second turn does not begin at a pristine 64,000. It begins with the previous turn's conversation already eating into it. You get a fresh budget and an immediately eroded one, which is a strange combination to reason about and an easy one to get backwards.

The practical consequence: if you set a task budget expecting a per-task ceiling, and your harness auto-continues, you do not have one. Decide deliberately whether your continuation carries tool results, because that single structural detail is your budget boundary.

Finding 3: the countdown is the one number you cannot read back

There is no task_budget information in the response usage object. The SDKs have no accessor for it. The countdown marker is injected server-side, into the prompt, for the model.

I find this genuinely unusual as an API shape. You set a parameter, the model acts on it visibly, and the only way to know where it stands is to reconstruct it yourself by summing usage.output_tokens across every request in your loop and adding the tokens of the tool results you appended between them.

python
spent = 0
for response in loop_responses:
    spent += response.usage.output_tokens
# then add the token count of every tool_result you appended

That reconstruction is useful for choosing a budget. It is not a live read of the countdown, and as the next two findings show, feeding your reconstruction back to the server is usually a mistake.

Python

Finding 4: the budget counts what the model saw, not what you sent

In an agentic loop your client resends the whole conversation every request, so your payload grows monotonically. The budget does not track that. It decrements only by content the model has not already seen: what it generates, plus genuinely new tool results.

Anthropic's worked example makes the gap concrete. Across three requests in one turn, the client transmitted roughly 20,820 input tokens cumulatively, while 19,000 tokens were counted against the budget. The original user message was sent three times and counted once.

This is the right design, and it is also why a client-side estimate built from request payload sizes will drift away from the real countdown almost immediately. Count generated tokens and new tool results. Do not count your own retransmissions.

Finding 5: two independent reasons not to touch remaining

The task_budget object takes an optional remaining field, defaulting to total. It is tempting: you are already tracking spend, so why not tell the server?

Two separate things go wrong.

It makes Claude quit early. If you decrement remaining while also resending full history, you are subtracting tokens the server was already going to subtract. The model sees an under-reported budget, the countdown falls faster than it should, and Claude wraps up before it needed to. The failure mode looks like a lazy model. It is arithmetic.

It invalidates your prompt cache. The budget value participates in the rendered prompt. Change it per request and the prefix no longer matches entries created under the old value. So the well-intentioned bookkeeping costs you cache hits as well as output quality, which is an expensive combination on exactly the long loops where you cared about budget in the first place.

There is one legitimate case: when your own code compacts or rewrites history between requests, the server loses track of what was spent before the rewrite. Then you pass remaining. Two details are easy to miss there. Pass it on every subsequent request, not only the one that compacted. And when computing it, exclude anything still present in the messages you are sending, including any summary you added, because the server counts those itself.

Server-side compaction is different again: it does not reset the budget, and tokens the turn spent before the compaction still count against it.

Finding 6: the support matrix is not a ladder, and two sources I trusted had it wrong

Read from the live documentation on 2026-09-20, task budgets are supported on Opus 4.7, Opus 4.8 and Opus 5, plus the Fable and Mythos lines, and the feature-support table lists Claude Sonnet 5 as not supported. Opus 4.6 is not supported either.

So this is not a newer-means-supported ladder. Sonnet 5 is a more recent model than Opus 4.7 and does not have the feature. If you are choosing a model partly for this, check the table rather than reasoning from release order.

What makes this worth a section rather than a footnote is that I found two sources saying otherwise. Pydantic's Anthropic model documentation lists claude-sonnet-5 among supported models. So does the cached table in Anthropic's own bundled claude-api skill. Both are sources I would normally take at face value, and both disagree with Anthropic's live feature-support table.

I am not going to tell you those sources are simply wrong, because I do not know their measurement dates and a beta's support matrix can move in either direction. What I will say is that the primary documentation is the one to verify against, and that this is the second time this quarter I have watched a cached capability table drift away from the live one. Check the feature-support table on the day you need the answer.

Also on the availability axis: task budgets are not supported on Claude Code or Cowork surfaces. At least one widely-linked guide currently documents a /config task_budget command for Claude Code. The live documentation says to use task budgets directly through the Messages API on a supported model.

Anthropic

Finding 7: a budget that is too small reads as a refusal

The minimum accepted total is 20,000 tokens, and anything smaller returns a 400.

Above that floor there is a softer failure that is much harder to diagnose. When Claude sees a budget clearly insufficient for the work, it may decline the task outright, scope it down aggressively, or stop early with a partial result rather than start work it cannot finish. Anthropic's guidance is blunt about the diagnostic order: if you see unexpected refusals or premature stops after setting a budget, raise the budget before debugging other parameters.

That inversion is the useful part. The symptom presents as a prompt problem or a model-capability problem, which is where most of us would look first, and the cause is a number you set in output_config. I have written before about how the same loop degrades as context fills, and this belongs in the same mental bucket: behaviour that looks like the model getting worse, caused by a knob.

The sizing advice that follows from it is to measure first. Run a representative sample without a budget set, record the distribution of per-task spend, and start from the p99 rather than a round number. I started from a round number.

Here is the shape that works:

json
{
  "model": "claude-opus-5",
  "max_tokens": 128000,
  "output_config": {
    "effort": "high",
    "task_budget": { "type": "tokens", "total": 64000 }
  }
}

The beta header is task-budgets-2026-03-13. Use streaming, because a max_tokens that large will otherwise run into HTTP timeouts. And note that effort and task_budget are doing different jobs: effort tunes how deeply Claude reasons per step, the budget tunes how much total work it does across the loop. Depth and breadth. If you are picking between models on reasoning behaviour, the axes that actually shift when you swap are worth reading alongside this.

What I did not verify

  • I did not run a paid loop specifically to measure the turn reset empirically. The mechanism is read from Anthropic's task budgets documentation, and my own overspend is consistent with it, but I did not instrument a controlled before-and-after.
  • I did not measure how much of a fresh turn's budget the previous turn's retained history actually consumes. The documentation says it counts; I have no figure for how much it matters in practice.
  • I did not test the 400 below 20,000 myself.
  • I have not established whether Pydantic's supported-model list reflects an earlier state of the beta or a current error. I checked both against the live table on one date and reported the divergence rather than adjudicating it.
  • I have not run a Managed Agents session against its dollar budget to watch budget_reached and the settle-events-only behaviour firsthand. That part is documentation plus the shape of the events table.
  • For a thorough treatment of the effort and thinking side of this, which I deliberately did not duplicate here, Hidekazu Konishi's write-up covers the parameter comparison in more depth than I do.

Postscript: my loop had been quietly buying itself a new budget every time it politely finished, which is either a bug or the most well-mannered cost overrun I have shipped.

D

Written by

Dani Reyes

Frequently asked questions

Does task_budget limit the total cost of a task?

No. It limits one agentic turn. A user message that carries no tool_result blocks starts a new turn with a fresh budget, so a harness that auto-continues after end_turn can spend the budget several times over on what you think of as a single task. Use max_tokens for hard per-request enforcement, and on Managed Agents use a session budget for a real spend cap.

How do I read how much task budget is left?

You cannot read it from the API. There is no task_budget field in the response usage object and the SDKs expose no accessor for it. The countdown is injected server-side and is visible only to the model. To track spend yourself, sum usage.output_tokens across every request in the loop and add the tokens of the tool results you appended between requests.

Should I pass the remaining field to keep the countdown accurate?

Usually no. If you decrement remaining while also resending full conversation history, the model sees an under-reported budget and wraps up earlier than it needed to. Changing the value per request also invalidates any prompt cache prefix containing it. Pass remaining only when your own code compacts or rewrites history, and then send it on every subsequent request.

Which Claude models support task budgets?

Read from the live feature-support table on 2026-09-20, task budgets are supported on Opus 4.7, Opus 4.8 and Opus 5 plus the Fable and Mythos lines, while Claude Sonnet 5 and Opus 4.6 are listed as not supported. Support does not follow release order, and at least two secondary sources disagree with the live table, so verify against Anthropic's documentation on the day you need the answer.

AI dev workflow

Context rot: two guards, split by model, and one model gets neither

Context rot is real and Anthropic now names it in its own docs. The useful question in 2026 is narrower: your Claude model ships with one of two self regulation mechanisms, or with neither, and they do not overlap. Sonnet and Haiku get context awareness free and automatic. Opus needs a beta header. Opus 4.6 gets nothing.

12 min read39
AI dev workflow

Claude Opus vs Sonnet: what breaks when you swap the model string

Comparisons of Claude Opus, Sonnet and Haiku score capability, speed and price per token. None of them mention that the model string is part of your request contract. Swap it and thinking can turn itself on, your answer can move out of content[0], and a max_tokens you never touched can start truncating. Eight measured differences, September 2026.

13 min read32

Context engineering is four mechanisms, not one

Context engineering on the Claude platform is not a setting. It is four mechanisms at four points in the pipeline, and the one everybody enables first pays for context window with prompt cache.

14 min read7