Security

A Circuit Breaker the Agent Can Read: What the Model Sees at Trip

Antonio Evans8 min read

A two-panel diagram: an orchestrator with a breaker switch flipped open beside a model context window whose newest bubble is a small JSON tool result with fields for code, target, signal, threshold, and next step.
Key points

Key points

  1. 1When a breaker trips, the agent receives a tool result, so that result is the message that decides whether the next step is a repair or a retry.
  2. 2A useful trip payload names the breaker code, the failing target, the tripped signal, the threshold and window, and one bounded next step.
  3. 3Ambiguous or partial tool feedback is a known cause of repeated tool calls, so fixing the returned text is cheaper than adding more turns.
  4. 4Put the single actionable field first, because the model weights the opening of the observation when it forms its next plan.

What the breaker actually hands the model

A circuit breaker is a state machine that wraps calls to a dependency. It stays closed while calls pass, flips open after a threshold of failures, and allows a single probe after a cooldown before returning to closed. The widely copied version comes from Michael Nygard's pattern catalog and is described in the agent circuit breaker pattern entry, which sets an example threshold of three failures and a 60 second cooldown.

All of that machinery lives in the orchestrator. The model never sees a state variable, a failure counter, or a diagnostic timestamp. What lands in the conversation is a single tool result: the content string that matches the tool call id, appended to the message history. Everything the harness cares about is invisible. Everything the model reasons over is in that string.

This matters because the harness, not the model, is where most of the engineering happens. Oracle's breakdown of the agent loop separates the model (inference) from the harness (context assembly, tool execution, constraints, state) and notes that most agent engineering work sits in the harness. The trip payload is a harness artifact. Treat it as an API for the model.

The failure mode we are trying to prevent is specific: the agent reads the blocked call as a transient problem and re-forms the same request with new wording. One practitioner quoted in a guide to timeout and breaker patterns for agent workflows described it plainly: per-call timeouts worked, but the agent treated a timeout as "try a different approach" and kept running. The breaker did its job at the infrastructure layer while the model's reasoning layer sailed past it.

Why "sorry, that failed" produces a reworded retry

Four properties of the trip message push a model toward re-rolling the same call.

It is ambiguous. A write-up on stopping reasoning loops names "ambiguous tool feedback" as a root cause of loops, alongside unclear goals and missing stopping criteria. A message like "The tool call failed. Please try again." is exactly that: partial, non-terminal, and suggestive of another attempt.

It implies the failure was transient. If the message describes a timeout or a 500, the model has been trained by experience to retry, because those often do resolve. A breaker-open result is not transient in the same way. It represents a breaker decision, not the last raw error.

It carries no state change. The model's plan was built on the assumption that the target is callable. If the tool result does not say the target is now unavailable for a defined window, the assumption survives and the plan does not change.

It buries the actionable part. Long outputs that open with apology, stack traces, or an embedded upstream error body push the one fact that matters past the part the model is most likely to act on.

Ambiguous feedback compounds. A study of infinite agentic loops analyzed 6,549 LLM agent repositories with a static tool and confirmed 68 loop failures across 47 projects, at 91.9% precision. The authors define the failure as a feedback path that repeatedly triggers model calls, tool invocations, or workflow transitions without an effective termination condition. They also note that framework limits such as max iterations exist but are often missing, misconfigured, or placed outside the actual feedback path. A trip message is part of that feedback path.

The trip payload contract

Return a short structured block with these fields, in this order:

FieldPurposeExample
actionThe single safe next step, stated firstswitch_to_fallback
breakerStable code naming which breaker firedpayments_api_v2
targetThe resource the agent calledPOST /v2/charge
signalWhat tripped it, in plain termserror_rate
observedThe measured value with a time window9/10 failed in last 60s
thresholdThe rule that was crossedtrip at 3 failures in 30s
stateBreaker state and when it changesopen until 14:02:10Z, then half_open
do_notThe specific behavior to avoidretry same endpoint

A stable breaker code matters more than it looks. Agents recover better when the observation is machine-parseable and consistent across turns, and it lets you join model behavior to your breaker metrics later. Give it a name a human would recognize in a log line, not a hash.

The signal field is where most teams lose the plot by pasting raw errors. Signal should be a short category: error_rate, latency, auth_failure, quota_exhausted, malformed_response. Each implies a different repair. An auth failure means the agent should stop and escalate rather than route around, because retrying with a different endpoint will not fix a credential. An error rate means fall back. A latency trip means reduce payload or await recovery. A guide to LLM tool calling error handling makes the same routing distinction for tool errors: transient infrastructure faults belong to the orchestration layer and should not be surfaced to the model, while input validation and application-level logic failures belong to the model, which must reason and correct its request.

That split is the clearest decision rule in this whole design:

  • Transient network fault: retry silently inside the harness, do not tell the model.
  • Breaker tripped: tell the model, with the full contract, because the plan itself must change.
  • Bad input from the model: return the schema error and let it correct itself.

Worked example: one call, two payloads, different plans

Take an agent that reconciles invoices. Its plan is: read the ledger, call the payments API to charge, then write to the warehouse.

The payments API starts failing. The breaker opens after three failed probes.

Payload A, the weak version:

{
  "error": "Tool call failed. The payments API returned an error. Please try again."
}

What the model sees: a failed call with no persistent state. It has no reason to abandon the plan. A plausible next step is to call the same tool with slightly different parameters, which the breaker then blocks again. The orchestrator absorbs two more trips, and the model spends tokens reasoning over errors that add no information.

Payload B, the contract version:

{
  "action": "switch_to_fallback",
  "breaker": "payments_api_v2",
  "target": "POST /v2/charge",
  "signal": "error_rate",
  "observed": "9/10 failed in last 60s",
  "threshold": "trip at 3 failures in 30s",
  "state": "open until 14:02:10Z, then half_open",
  "do_not": "retry same endpoint before half_open",
  "fallback": "payments_api_v1, same request shape"
}

Now the model has a state change, a deadline, a named alternative, and a prohibition. The natural next step is a different tool call to a different endpoint. The plan is repaired rather than repeated.

The numbers do the persuasive work. "9/10 failed in last 60s" tells the model this is not a blip. "trip at 3 failures in 30s" tells it the harness will not let it brute-force through. "open until 14:02:10Z" tells it time is the constraint, not wording.

One caution on the timestamp: a concrete time is useful, but only if your clock is trustworthy and the agent does not have to compute durations. Prefer including both the absolute time and a relative hint ("about 40 seconds from now") so the model does not have to do arithmetic across time zones.

Keep it short and put the action first

The trip payload competes for attention with the whole history: the user request, the system prompt, prior tool output, the plan. Field order is not cosmetic.

Put action first. Models condition on the opening of an observation when deciding what to do next, and the first token of a repair-shaped observation is more likely to produce a repair. Bury the next step in paragraph four and a model may treat the payload as a status note.

Keep the total payload small. Every trip message is re-sent in context on the next turn, and on every turn after, so a verbose diagnostic block taxes the whole remainder of the run. Strip stack traces, upstream request ids, and full response bodies. Log those in the orchestrator. The model needs the state and the next step, not the evidence trail.

There is a real cost tradeoff here. One analysis of operational breakers for agents warns that validation overhead, the verification work needed to catch failures that look like successes, can exceed 200% of execution cost. That is an argument for a payload that is cheap to produce and cheap to keep: a fixed set of fields drawn from data the breaker already tracks, not a generated narrative.

Set a hard length budget in the harness. If the rendered block exceeds it, something is wrong with the payload generator.

Cases where the actionable field is not "fall back"

The contract should not assume every trip ends in a fallback. Four signals, four different next steps:

SignalRecommended actionReason
error_rateswitch_to_fallback with a named alternativePersistent failures mean route around
latencyreduce_payload or await_recoveryThe target is alive; the call is too heavy
auth_failurestop_and_escalateRetrying cannot fix credentials
quota_exhaustedawait_recovery with reset hintRetrying makes it worse
malformed_responsestop_and_escalateDo not loop on garbage

Auth and malformed-response trips are the ones teams most often mis-label as retryable. A practical guide to error handling for LLM calls groups 4xx client errors as "your fault, do not retry" and 429 and 5xx as retry candidates. A breaker carries the same distinction one level up. If your breaker trips on an auth failure, the payload must say so, or the model will do what it does with any error: try again.

Escalation is a legitimate agent outcome, not a failure of the automation. Research on triaging agent failures by priority puts crashes, stuck loops, wrong output, and rate limits on different urgency profiles, and rollback or corruption cases above all else. Your action field should map onto that same ladder so an operator reading logs later can see what the agent was told.

Wiring it up, and measuring whether it works

The payload should be generated by the same component that owns the breaker, so the state and the observation can never drift apart. A sketch:

def breaker_result(breaker, call, decision):
    return {
        "action": decision.next_step,
        "breaker": breaker.code,
        "target": call.endpoint,
        "signal": decision.signal,
        "observed": decision.observed_text,
        "threshold": breaker.threshold_text,
        "state": decision.state_text,
        "do_not": decision.prohibition,
        "fallback": decision.fallback_name,
    }

Then test it. Measure three things per breaker code:

  • Repeat rate. How often the model calls the same tool with the same arguments after an open trip. The baseline is usually worse than teams expect, and this number is your primary success metric.
  • First-action match. How often the model's next tool call matches the action field. Low values mean the payload is being read but not trusted, often because the field order or wording is weak.
  • Turns to recovery. Median remaining steps after a trip. This catches improvements that reduce repeat calls but leave the model wandering.

A before-and-after comparison on one breaker, run over a few hundred trips, tells you more than any amount of prompt debate. Keep a copy of the old payload variant and A/B it.

Where the human side matters, the same discipline applies. On-call practice already expects that every page needs a clear, actionable message, and that escalation paths never dead-end; a guide to on-call schedules and handoffs treats a clean handoff and a non-dead-ending escalation policy as core mechanics. A breaker trip is the machine equivalent of a handoff, and it fails the same way when the recipient cannot tell what to do next.

If your team is designing the model layer and the orchestration layer together, especially across more than one model vendor, Quanta Inference puts text, image, video, audio, and embedding models behind one OpenAI-compatible endpoint, which keeps the trip payload contract identical regardless of which model answers next.

FAQ

FAQ

What exactly counts as a circuit breaker trip in an agent system?

It is the moment your orchestrator's breaker moves to the open state after crossing a failure threshold, so the next call to that target is blocked without being attempted. The tool result you return at that moment should describe the decision, not the underlying exception. For a short window, the model cannot make progress against that target no matter how it words the request.

Should the payload include the original error message from the dependency?

Generally no, or only a one-line summary. The original error is evidence for humans and belongs in your logs; the model needs the state change and the next step. Including a raw stack trace invites the model to reason about the dependency's internals, which it cannot fix, and lengthens every subsequent turn in the run.

How long and how machine-readable should the trip payload be?

Short enough to stay under a fixed length budget you enforce in the harness, and structured enough that the fields parse. JSON with a stable set of keys works well because it is unambiguous and easy to diff in logs. If your agent stack does better with a labeled plain-text block, keep the same field order and names.

Does a clearer payload reduce token spend, or just change behavior?

Both, though the behavior change comes first and is easier to measure. Fewer repeat calls means fewer turns carrying a bloated context, and every avoided turn removes the full history from the next request. Measure repeat rate and turns-to-recovery separately so you can see which effect you are getting.

What if the model ignores the next-step field anyway?

Check three things in order: field order (is the action first?), whether the state field gives a concrete time or window, and whether the fallback you name actually works. After that, add a hard stop in the harness, such as blocking duplicate calls within a short window, because a debounce hook demo shows the harness can cancel a duplicate call and return a blocking message to the model rather than relying on the prompt alone.

Sources

Sources

  1. Agent Circuit Breaker
  2. The Agent Loop Decoded | developers
  3. AI Agent Timeout & Circuit Breaker Patterns | 2026 Guide
  4. How to Prevent AI Agent Reasoning Loops from Wasting ...
  5. Uncovering Infinite Agentic Loops in LLM Agents
  6. LLM Tool Calling Error Handling: Retries and Fallbacks
  7. Resilience Circuit Breakers for Agentic AI
  8. Error Handling & Retries: Making LLM Calls Reliable
  9. How to Triage AI Agent Failures by Priority | AgentCenter
  10. On-call best practices: handoffs, schedules, and alert fatigue
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