A failed agent run is billed in full
An agent run that ends in an error still bought every token it spent. The turn an error surfaces on, and how retries are classified, set what it cost.
A run that ends in an error has still bought every token it spent. The provider charges for the turns that happened, not for the ones that produced an answer, so what a failure costs is set by two things: how far into the loop the error surfaced, and how many times the failure was retried before anyone gave up. Both of those are decisions in your own code, and neither is visible in a cost figure that only counts runs which finished.
Three fixes landed in one framework in the same week, each on a different part of that path.
The rejection that arrives last
A CrewAI pull request opened on 2026-09-14, changing how the forced final answer is sent, describes a deterministic rejection that can only be discovered at the end:
When an agent hits
max_iter, the final "give your best answer" instruction was appended as an assistant message and sent as assistant prefill. Current Claude models (Opus 5, Sonnet 5, Fable 5.x, the 4.6+ family) reject that with400 This model does not support assistant message prefill, and only after the whole iteration budget has been spent.
Read the shape rather than the bug. The request that gets refused is malformed in a way that does not depend on the input: the same code path emits the same unsupported message shape every time, against models that have never accepted it. A check on the request shape could have raised it before the first call. Instead it is reachable only on the path where the iteration cap is hit, which is the most expensive run in the distribution — every turn up to the cap is paid for, and then the run ends with nothing to show.
The refused call itself is the cheap part; it is one request. What the defect cost is everything before it. That distance — between the earliest point a failure was knowable and the point it actually surfaced — is the quantity, and it is measured in turns, not in error rates.
A retry is a bet that the next attempt differs
A second pull request, opened on 2026-09-13, separates provider errors from validation failures in CrewAI's LLM guardrail, and states what collapsing them did:
LLMGuardrail.__call__caught every exception and returned(False, "Error while validating..."), making infrastructure errors (provider outage, rate limit, expired key) indistinguishable from validation failures.
The three consequences it lists are, in order: "Provider error text appended to
agent conversation as a user turn", "Up to guardrail_max_retries unnecessary
re-executions during outage", and "Misleading 'guardrail failed validation'
error after retries exhausted".
A retry is an economic bet that the next attempt has a different outcome. When the model's answer failed a content check, that bet is reasonable — a resample may pass. When the provider is down, the key has expired, or the request shape is unsupported, nothing about running it again changes the result, and the retry budget is spent entirely on the class of failure whose success probability is zero. One boolean cannot tell those apart, so the retry policy cannot either.
The first consequence in that list compounds the second. If the provider's error text is appended to the conversation as a turn, each re-execution starts from a slightly larger prompt than the one before it, and the failed attempts are not even the same price as each other.
A bound in the unit the failure consumes
The third fix, from the same day,
adds a poll timeout to a CrewAI tool
that "previously polled Contextual AI status in an unbounded
while True: sleep(5) loop", so that "If an upstream job hung or took an
indefinite amount of time, the tool call blocked the agent indefinitely". The
fix adds poll_timeout (default 300s) with time.monotonic() deadline
tracking.
That failure costs wall-clock time rather than tokens — the tool is not the model — which is exactly why it is worth putting next to the other two. The move is the same in all three: an unbounded failure mode gets a ceiling, and the ceiling is expressed in the unit that failure actually consumes. For a polling loop that unit is seconds. For a loop that keeps calling a model, it is money, and a limit counted in iterations is only a proxy for it.
A comment in a Hacker News thread on 2026-09-12, asking what companies do to control enterprise coding-agent cost, puts the general case plainly:
the cost that got away from us was never the big deliberate runs, it was the default behaviours nobody had looked at
A retry count, a guardrail's exception handler and an iteration cap are all defaults, usually set once at the top of a class definition, and all three decide what a bad day costs.
The arithmetic, over assumed numbers
None of the following is a measurement. It is arithmetic over inputs you would have to supply from your own system.
Take a run capped at 25 turns, wrapped in a guardrail configured to retry three times. A deterministic failure that can only surface at the cap costs 25 turns per attempt and four attempts, so 100 turns are billed and no run completes. The same defect caught at turn one costs one turn and produces the same error message. The difference is not the model, the prompt or the provider — it is where the check sits.
Now put ten of those runs next to ninety that finished. If you report cost per run over the runs that completed, the ten failures disappear from the denominator while their spend stays on the invoice, and the metric gets better the more often the loop fails at the cap. That is the same denominator problem as deciding what counts as one run, arriving from the failure side.
What to record
- Spend on runs that ended in an error, as its own line. Not netted into an average, and not dropped: it is real money attached to zero completed work, and it is the only number that makes a retry policy arguable.
- The turn index the terminal error surfaced on. A distribution of that number tells you which failures are cheap and which ones are only reachable after the whole budget is gone.
- Attempts, with the class of failure that caused each one. Infrastructure, content, and malformed-request are three different bets. Recorded as one boolean they are indistinguishable afterwards, exactly as they were at the moment the retry was decided.
- Which agent was running when it failed. In a multi-agent run the failure path belongs to one sub-agent and the bill belongs to the run, and the two are only connectable if identity was attached when the call was made.
- A ceiling, because the failing turn is not predictable. You cannot know in advance which run hits the cap; you can decide in advance what the attempt is allowed to spend before it is refused.
Spend with nothing to show for it is the same accounting problem as the answers you discard — the calls are well formed, the tokens are real, and the client-visible output is not there to divide by.
How this works in Capsera
Capsera does not see your exceptions. It stores hashes and token counts, never
prompt or completion content, so it cannot tell you that a run ended on a
400 or that a guardrail misread an outage — the terminal state of a run is
your application's to record, and the list above is written on the assumption
that you record it.
What it does give you is the spend side at the grain the failure happened on.
capsera.init() patches the provider clients — Anthropic messages, OpenAI chat
completions, OpenAI embeddings and Google Gemini, sync and async — so every
call in the process is captured whether or not the run it belongs to ever
returns an answer. @capsera.agent(name="reviewer") adds who, not whether, and
because the agent stack is a ContextVar rather than thread-local state,
sub-agents inherit identity and the calls made underneath a failed branch still
report as themselves. Cost per run therefore includes the runs that failed,
which is the point.
The ceiling is the part that does not require predicting the failing turn. Budgets are checked before the provider call and scoped per agent, per team or globally, so an agent that has started re-executing a request that will never be accepted is refused at its envelope rather than at the end of its retry count. When no budget matches, or when Capsera itself is unreachable, the call proceeds — the governance layer is not allowed to become the reason a run fails.
Two places to look elsewhere. If the question is what the failing request actually contained — the message that was rejected, the exception text, the full trace of the attempt — that is content inspection, and Langfuse is open source, free and built for it; Capsera is not, by design. And if you want retry and fallback policy applied centrally across providers rather than inside each framework's own handler, that is a gateway's job: LiteLLM is MIT-licensed, free, covers 140+ providers and enforces hard budgets per key, team, organisation and model.
Give every agent an identity, a budget, and hard limits.
One line of code. Anthropic, OpenAI, and Google Gemini.
See pricing