AI agents

A Downgrade Path When a Model Overruns Its Output Budget

Antonio Evans11 min read

A split diagram: on the left, a request box with a token counter that has gone past a red budget line.
Key points

Key points

  1. 1An overrun is a routing decision made after the call, using what the response actually produced, not a setting you should have guessed before sending.
  2. 2Retry with a hard truncation only when the excess is small and the task survives a shorter answer, and never retry the same prompt unchanged.
  3. 3Fall back to a different tier when the answer is incomplete at the cut point, or when the cost of a second full call still fits the same envelope.
  4. 4Refuse and return the partial output plus a stable error code when the task needs the whole answer and no cheaper tier exists.

What an overrun actually tells you

Most budget talk happens before the call: you pick a model, size the prompt, and set a maximum output length. The overrun is the moment after that, when the response stops with a length finish reason and the text ends mid-sentence, mid-object or mid-tool call. The first thing to record is not the failure but the shape of it: how many tokens the answer used before the cut, whether the stop landed inside a structured structure (valid JSON that fails to parse, or a tool call with missing arguments), and whether the visible content already satisfies the user.

That distinction matters because a truncated response is not automatically useless. If the cut lands after the final paragraph of a summary, the reader has what they asked for and the extra tokens were padding. If the cut lands inside a function call, the caller may be holding a syntactically valid JSON object with a missing argument, which is worse than an obvious error. The tool calling literature is explicit that models can emit structurally valid JSON with nonsensical or missing parameter values, so schema validation is the only reliable gate, not the finish reason alone, as described in this overview of LLM function calling.

A useful first classification runs in three cheap steps:

  1. Read the finish reason. If it is a normal stop, there is no overrun even if the answer is long.
  2. Validate the payload against its schema. A parse failure or a missing required field turns the cut into a hard error, whatever the text looks like.
  3. Score completeness against the task type. A summary, an explanation, or a first draft tolerates a cut. A migration script, a data extraction, or a tool call does not.

Only after those three steps should you choose between the three usable paths: retry with a hard cap, fall back to a different tier, or refuse with the partial output attached.

Why the pre-call estimate fails

Length estimates fail because models are poor at tracking their own output length. A study of dynamic length feedback found that while language models measure short texts accurately, that ability declines significantly as text length grows, and the authors propose interrupting generation to feed the running count back to the model as a correction (the length tracking study). In other words, the model does not know it is about to overshoot, so asking it to stay within 800 tokens is a soft signal rather than a guarantee.

The same effect shows up in reasoning tasks, where chain-of-thought output can balloon well past what the question needs. The token-budget work on reasoning finds that the reasoning process is often unnecessarily long and can be compressed by stating a budget, but the choice of budget is critical to whether compression helps or hurts (token-budget-aware reasoning). A budget that is too tight produces a worse answer than no budget at all, which is exactly why a post-call decision is safer than a pre-call guess.

There is a second structural reason estimates miss: the context window is shared between input and output. If the model's window is generous but your prompt, history, and few-shot examples eat most of it, the remaining room for the answer is smaller than the model's headline maximum output. The context window guide spells this out with model-level numbers and shows how to split the total window across system prompt, examples, and user input before spending any of it on output (the context window guide). Budget the whole window, then treat the output share as an estimate you are prepared to revisit.

The three responses compared

Each path has a distinct cost and quality profile. The table below is a starting decision aid, not a law; calibrate the thresholds with your own traffic.

SituationBest responseExtra costExtra latencyAnswer quality
Cut is small, text reads as completeAccept with a warning headerNoneNoneFull for practical purposes
Cut is small, task tolerates a shorter answerRetry once with a hard cap and a length hintOne more callOne round tripSlightly reduced, usually acceptable
Cut is large, or payload is invalidFall back to a bigger-budget tier or a cheaper model that finishesOne more call, possibly at a higher output rateOne round tripFull if the fallback model is capable enough
Payload invalid and no acceptable fallbackRefuse, return partial plus error codeNoneNonePartial, and clearly labeled
Task is deterministic and cannot be summarizedRefuse, or re-request in chunksDepends on chunkingMultiple round tripsFull if chunking works, risky if it does not

The CrewAI community thread on this exact problem is worth reading because it describes the failure honestly: a user hits a model's output ceiling on content that cannot be summarized and asks whether re-prompting from the last lines to continue is a hack (the CrewAI discussion). It is a reasonable hack for prose. It is a poor idea for JSON, where the continuation has to thread back into an open object and most models will re-open or re-close it wrongly.

Worked example: a retry that is worth it

Suppose a call is allowed 1,200 output tokens and comes back at 1,240 with a finish reason of length. Assume the text ends after a complete paragraph and the remaining sentence is filler. Here the excess is 40 tokens, about 3 percent. A retry with the same 1,200 cap and an instruction to be more concise will almost certainly land under the cap, because the model was already close. The cost is one extra call at the same price per output token, and the acceptance criterion is simple: the retried answer parses and contains every required heading. If it fails that test, do not retry a third time.

Worked example: a fallback that pays for itself

Suppose a call is capped at 800 output tokens but the task is a comparison table with five rows and the model needs roughly 2,000 to finish. Retrying at 800 cannot succeed, because the answer is structurally larger than the cap. The right move is a fallback to a tier with a 4,000-token ceiling, or to a model whose output price is low enough that a longer answer still fits the original envelope. That second calculation is where per-token pricing matters. If the primary model charges a high rate for output and the fallback charges a fraction of it, a 2,000-token answer on the cheaper model can cost less in total than a 1,200-token answer on the primary one. Check the published per-token rates for your models, as in the OpenAI pricing page, and compare total cost rather than price per token.

That is the insight most first-pass implementations miss: a downgrade tier is not always cheaper per answer. It is cheaper per unit of work done. A long answer on a cheap model can undercut a truncated answer on an expensive one, and a short answer on the expensive model can undercut a long answer on the cheap one. Decide using total cost for the completed task, not the headline rate.

Retry with a hard truncation

A retry is only worth spending when it carries new information. The Hermes agent issue tracker documents the failure mode precisely: the first retry re-runs the identical request with no additional context, so it almost always truncates again for the same reason, and the agent then gives up (the truncated tool call issue). The proposed fix is a second retry that injects a hint asking the model to use concise arguments or split large content into smaller writes.

That is the right shape for a retry policy:

attempt 1: original request, original cap
  if finish_reason == stop and schema valid: return
attempt 2: same cap, plus hint
  "Your previous answer was cut at the limit.
   Produce the same content in under N tokens.
   Drop examples, keep required fields."
  if finish_reason == stop and schema valid: return
else: escalate to fallback or refuse

Three rules keep this bounded. First, cap the number of retries at one, or two with a hint. Unbounded retries on the same prompt burn spend with no new signal. Second, never retry with a larger cap and call it a retry; that is a fallback with the same model, and it should be priced as such. Third, require schema validation on the retry, so a shorter but malformed answer does not slip through. The function calling guide recommends exactly this: wrap parsing in try-catch, validate against the original schema before execution, and return structured errors that let the model self-correct on retry (the function calling overview).

Hard truncation has one quality risk worth naming. If the model honors the shorter cap by dropping content rather than compressing it, the answer looks complete but is missing a required section. A word-count check on the retried answer plus a required-fields check catches most of this. If either fails, treat it as a refusal case, not a success.

Falling back to a different tier

Fallback is the answer when the correct response simply does not fit the cap. The operational pattern is well established: define triggers, keep a diverse set of backends, bound retries, and monitor every route so you know how often the fallback fires and whether it recovers (the LLM fallback guide).

An overrun is a fallback trigger that most gateways do not model. The usual triggers are errors, timeouts, and rate limits. Adding "finish reason is length and payload invalid" to that list is a small configuration change with a large reliability payoff, and it lets the gateway own the decision instead of every caller reimplementing it.

Cascades are the formal name for this pattern. A cascade sends requests to a cheap model first and escalates to a stronger, more expensive model when the cheap answer fails a quality check. The routing literature notes that frontier models can cost roughly 10 to 50 times more per token than a well-tuned smaller model, and that routing the majority of traffic to the cheaper tier while preserving quality can cut inference spend by 60 to 70 percent (the model routing chapter). An overrun-driven downgrade is a cascade run in reverse: the call started high and moves to a tier that can finish the job within budget.

Two decision rules make this concrete:

  • Escalate on necessity. If the task needs the full answer and the current tier cannot produce it within the cap, fall back to a tier with a larger ceiling. Do not negotiate.
  • Downgrade on cost. If the task tolerates a longer answer but the current tier's output price makes that answer expensive, fall back to a cheaper model with a larger output ceiling and accept the quality difference if your evals show it is small.

Give the fallback a hard cap of its own, and record the reason for the route change with a marker such as the cause tags used in adaptive routers (the LiteLLM auto-routing docs). Without a recorded cause, you cannot tell a healthy fallback rate from a router that is misconfigured.

Refusing and returning the partial output

Refusal is the least popular path and often the correct one. It applies when the partial answer is dangerous to act on, when no fallback tier is available or acceptable, or when the task is deterministic and a shorter answer would be silently wrong.

A good refusal is not an empty 500. It returns three things: the partial output, a stable error code, and enough metadata for the caller to decide. A workable shape:

{
  "error": "output_budget_exceeded",
  "partial": "...text produced before the cut...",
  "detail": {
    "cap_tokens": 1200,
    "used_tokens": 1200,
    "finish_reason": "length",
    "schema_valid": false,
    "suggested_downgrade": "tier-b",
    "retryable": true
  }
}

The caller then chooses: show the partial answer to a human, re-request with a larger cap, or abandon. This is the same principle behind bounded retries and recovery paths in production orchestration guidance, which stresses that retries need limits, idempotency awareness, and an explicit recovery path (the orchestration guide). A refusal that hands back the previous attempt's state is a recovery path; a refusal that discards it is a dead end.

Refusal is also the right default for anything a downstream system will execute. If a truncated tool call would have completed a write, a delete, or a payment, returning partial arguments as if they were whole is an incident waiting to happen. Treat schema invalidity on a tool call as non-negotiable: refuse, log, and let the agent retry with a hint or escalate to a tier with a larger cap.

Putting the policy in the gateway

The cleanest place for this decision is the gateway layer, because the gateway already sees the finish reason, the token counts, and the price of each model. Centralizing it means every service inherits the same policy, and the observability comes for free.

A gateway can route, retry, and fall back across providers, and the operational guidance for that layer recommends tracking fallback frequency, recovery success, latency, and cost per route (the LLM fallback guide). Extend that dashboard with one more metric: overruns per model per day. That single number tells you whether the caps are too tight, whether a model has started to ramble, or whether a prompt change quietly increased verbosity.

Keep the policy in configuration rather than code. Rules like "if finish reason is length and schema invalid, route to tier B once, then refuse" are readable by anyone on the team and easy to change when a model's behavior shifts. The signal-driven routing paper makes the same case from first principles: opposite deployment constraints should be expressed as different configurations over the same architecture, not as different code paths (the vLLM semantic router paper).

If you are wiring this together across several providers, one API key for every model family removes a full layer of per-provider handling and keeps the overrun policy in one place: Quanta Inference exposes text, image, video, audio and embedding models behind an OpenAI-compatible endpoint, priced in US dollars, so the fallback table does not need a different client for every tier.

Calibrating the thresholds

The table earlier gives qualitative rules; production needs numbers. Calibrate with four measurements per task type:

  1. Cut frequency. The share of calls that finish on a length reason. If it is above a few percent for a given task, the cap is probably too tight for that task.
  2. Cut severity. The median number of tokens left unproduced, estimated by re-running a sample without a cap. Small excesses favor retry; large ones favor fallback or refusal.
  3. Retry success rate. The share of capped retries that produce a valid answer under the cap. Below roughly half, retries are mostly wasted spend and you should go straight to fallback.
  4. Fallback quality delta. How often the fallback answer passes your evals compared with the primary tier. If the delta is negligible, make the cheaper tier the default for that task and skip the overrun entirely.

Run this for a week of traffic, then revisit quarterly. Thresholds drift as prompts change, as models are updated, and as traffic mixes shift toward harder or easier requests. The point of the policy is not to find the perfect number once; it is to make the overrun a decision you have already designed, so the event that used to be an outage becomes a logged route change with a known cost.

FAQ

FAQ

Is an overrun the same as a context window error?

No. A context window error happens before generation, when the prompt plus the requested output exceeds the model's total window and the API rejects the request. An overrun happens during generation, when the response stops at the output cap with a length finish reason. The first is a sizing mistake you fix by trimming the prompt; the second is a post-call decision about what to do with a partial answer.

Should I just set a very large output cap and avoid the problem?

A large cap removes the truncation but not the cost. You pay for every token generated, and the length tracking research shows models do not reliably stop early on their own, so a generous cap invites verbose answers that pad the bill without improving quality. Set a cap matched to the task, then handle the overrun when it happens. Use the largest cap only as the fallback tier.

Why not retry the identical prompt and hope for a different result?

Because the second attempt carries no new information, so it usually truncates in the same place, which is exactly what the Hermes issue documents. A useful retry changes something: a hint to be concise, an instruction to drop examples, or a request to split large content into several smaller writes. Without a change, you are paying twice for the same failure.

When is a partial answer good enough to hand back?

When the cut lands after the content the user actually needs and before filler. A summary whose final sentence is lost is usually fine; a structured extraction missing a required field is not. Use three checks: the finish reason, schema validity, and whether the last complete paragraph satisfies the stated task. If all three pass, return the partial with a header noting it was cut.

How does chunking fit into the downgrade path?

Chunking is a fourth option that sits outside the three main paths. It applies when content is deterministic and cannot be summarized, such as a long file write or a large data extraction: ask for the first section, then re-request with the last few lines as context for the next. It works for prose and is fragile for JSON, because the continuation has to thread back into an open object. Budget for several round trips and only use it when refusal is worse for the caller.

Sources

Sources

  1. What Is LLM Function Calling?
  2. Can LLMs Track Their Output Length?A Dynamic ...
  3. Token-Budget-Aware LLM Reasoning
  4. LLM Context Windows Explained: Token Budget Guide
  5. What are the strategies for handling outputs which exceed ...
  6. Pricing | OpenAI API
  7. Truncated tool call: second retry should inject concise-args ...
  8. What Is LLM Fallback? Meaning and How It Works
  9. Model Routing: Selection, A/B Testing, Cascades & ...
  10. Beta Auto Routing
  11. AI Agent Orchestration: Patterns, Workflows & Best Practices
  12. vLLM Semantic Router: Signal Driven Decision Routing for ...
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles