AI agents

Structured Output for Cap Exceeded: A Schema for Refusals

Antonio Evans8 min read

A two-panel diagram: a subagent box tripping a token-cap switch on the left, and on the right the JSON refusal payload with arrows from its reason_code and coverage fields into an orchestrator that branches to retry.
Key points

Key points

  1. 1A capped subagent should return a typed JSON envelope with a fixed reason_code, not a prose apology, so the orchestrator can branch on fields.
  2. 2The envelope carries a partial_result validated against the task schema, coverage counts, completed and remaining steps, and a retry_hint.
  3. 3The orchestrator's decision rule is a lookup on reason_code plus coverage, choosing between a bigger budget, a narrower scope, a task split, or escalation.
  4. 4Every envelope must be schema-validated at the boundary, because a schema-valid object can still be wrong, and a malformed one can silently corrupt downstream state.

Why prose returns break orchestrators

In a subagent pattern, a supervisor calls specialized subagents as tools and combines their results, which gives strong context isolation because subagents stay stateless the LangChain architecture guide. That isolation is only as good as the return contract. When the subagent hits a token cap, a wall-clock cap or a tool-call cap and then answers with something like "I ran out of room before finishing the third section," the orchestrator has to guess. Was the work 70 percent done or 20 percent done? Should it retry, split, or escalate? Prose answers all of those questions badly.

The failure is the same one teams hit when agents talk to each other in freeform natural language: a missing unit or an implied default compounds across a chain until a wrong answer arrives with no traceable origin the MLflow orchestration guide. The fix described there is to define typed input and output schemas for every handoff so a malformed payload fails validation instead of silently corrupting downstream state, and to constrain each agent to one valid action per turn from an enumerated set the same MLflow guide. A cap event is a handoff like any other. It deserves the same treatment.

There is a cost motive too. Multi-agent research systems use roughly fifteen times as many tokens as chat interactions, according to Anthropic's measurement as reported by ByteByteGo's writeup, and token usage alone explained 80 percent of performance variance on BrowseComp Augment Code's cost analysis. When each retry spends real money, an orchestrator that retries blindly because it could not tell a near-complete result from an empty one is burning budget on guesswork.

The envelope: fields and a closed reason_code set

Define one schema per task family and reuse it for both the success and the cap case. The difference between the two is the status field, not the shape. That way the orchestrator has a single parser and a single validator.

{
  "status": "capped",
  "reason_code": "TOKEN_CAP",
  "partial_result": { "sections": ["intro", "pricing"], "claims": 14 },
  "schema_ok": true,
  "coverage": {
    "items_expected": 40,
    "items_completed": 28,
    "unit": "sections",
    "fraction": 0.70
  },
  "steps": {
    "completed": ["outline", "sections 1-28", "claim extraction"],
    "remaining": ["sections 29-40", "dedupe", "final validation"]
  },
  "retry_hint": {
    "suggested_action": "SPLIT",
    "suggested_scope": "sections 29-40",
    "budget_multiplier": 1.0,
    "notes": "remaining work is independent per section"
  }
}

Field by field:

FieldTypeWhy it exists
statusenum: ok, capped, failedOne branch point for the orchestrator, no prose parsing
reason_codeclosed enum, see belowSelects the recovery policy
partial_resultobject validated against the task schemaLets the orchestrator salvage work instead of discarding it
schema_okbooleanSays whether partial_result passed validation before return
coveragecounts plus a fractionDistinguishes a near-miss from a thin sliver
steps.completed / steps.remainingarrays of step idsMakes a split or resume possible without re-planning
retry_hintobjectThe subagent's proposal; the orchestrator is free to overrule it

Keep the reason_code set closed. Six values cover most production caps: TOKEN_CAP, TIME_CAP, TOOL_CALL_CAP, CONTEXT_OVERFLOW, DEPENDENCY_FAILED (an upstream tool or API failed), and SCOPE_TOO_BROAD (the subagent concluded the task should be decomposed rather than attempted). Closed sets matter because they let you write an exhaustive decision table and let a validator reject unknown values. JSON Schema enums exist for exactly this, and schema validation is the contract that describes required fields, types and constraints before the data exists Agenta's structured output guide.

Enforcing the envelope with structured outputs and a validator

Provider-native structured output features enforce a JSON schema at generation time, while function calling has the model name the function and fill parameters in a validated format Agenta's guide to structured outputs and function calling. Use them, but do not stop there. A schema constrains shape, not truth, and provider behavior around refusals, truncation and fallback still matters TrueFoundry's structured outputs writeup.

One practical wrinkle: if your runtime only offers schema enforcement on the whole response and you also want free text in partial_result, ask for the envelope as a tool call and keep any narrative in a string field. That pattern is common enough that developers ask about forcing correct tool-call JSON while leaving the rest of the message free this OpenAI community thread.

Validation on receipt, in order:

  1. Parse the envelope. Reject on parse failure and mark the subagent run as a contract failure.
  2. Validate against the envelope schema. Unknown reason_code values fail here.
  3. If schema_ok is true, validate partial_result against the task schema. If validation fails, downgrade schema_ok to false and treat the partial result as absent.
  4. Check that coverage fractions are arithmetically consistent (completed never exceeds expected).
  5. Route violations to retry, repair or escalation logic, never straight to the user MLflow.

Grammar-constrained decoding can nearly eliminate syntax errors, yet structural and semantic errors persist, as an empirical study of structured output control found Song et al. on arXiv. That is the reason step 3 exists: the model can produce valid JSON that is not a valid answer to your task.

The decision rule: branch on reason_code and coverage

This is the part most teams get wrong. They branch on coverage alone and treat every partial result as retryable. The reason_code is what tells you whether more budget helps. A TOKEN_CAP at high coverage is a different problem from a SCOPE_TOO_BROAD at low coverage.

reason_codeCoverageRecommended actionRationale
TOKEN_CAP0.8 or higherResume with a larger token budgetThe agent was close; more of the same works
TOKEN_CAPbelow 0.8Narrow scope to steps.remaining, or splitLow coverage suggests the scope was wrong, not the budget
TIME_CAPanySplit by steps.remaining across parallel workersParallel fan-out cuts wall-clock time; do not buy more time on the same worker
TOOL_CALL_CAP0.6 or higherLarger tool-call budget, resumeRemaining steps are likely cheap calls
TOOL_CALL_CAPbelow 0.6Change the worker or the tools, then retryMore calls will not fix a tool that is not fit for the job
CONTEXT_OVERFLOWanySplit into sub-tasks with fresh contextEach subagent gets a fresh window by design, so decomposition is the fix
DEPENDENCY_FAILEDanyEscalate or retry after backoffThis is not the subagent's fault; buying budget wastes money
SCOPE_TOO_BROADanySplit, then re-dispatchThe subagent is telling you to decompose
schema_ok falseanyRepair once, then escalateUnvalidated results must not enter shared state

Two numbers make the table concrete. In Anthropic's multi-agent research setup, subagents running in separate context windows let the system spread reasoning in a way a single agent could not, and the orchestrated system outperformed a single agent by a wide margin on internal research evaluations, as summarized by ByteByteGo. The gain comes from decomposition, which is exactly the action the table prefers when coverage is low or the context overflows. Meanwhile, multi-agent orchestration adds latency at every handoff and requires explicit state management between components, which is why Microsoft advises using the lowest complexity that reliably meets requirements Microsoft's Azure Architecture Center. A cap envelope is state management in its cheapest form: it is a message, not a new agent.

The rule in one sentence: coverage decides whether to resume or split, and reason_code decides whether more budget could possibly help. If the reason is not a budget cause, spend nothing.

A worked example with numbers

Suppose a subagent is asked to extract 40 structured product records from a document corpus, with a token cap of 60,000, a wall-clock cap of 10 minutes, and a tool-call cap of 100.

Run 1 returns TOKEN_CAP with coverage 0.35 (14 of 40 records) and an empty remaining-work list beyond "the rest of the corpus." Under the table, 0.35 is below 0.8, so the orchestrator does not buy a bigger budget. It splits: four workers, each assigned 10 records. Cost of the decision: one envelope validation and one planning call.

Run 2: three workers return ok, one returns TIME_CAP at coverage 0.60 (6 of 10 records) because a source was slow. The four remaining records sit in steps.remaining, so the orchestrator resumes that one worker with a narrower scope of exactly those four records rather than re-running all ten. Rough saving: it re-does four records instead of ten, a 60 percent reduction in that worker's repeated work.

Run 3: one record returns DEPENDENCY_FAILED because the source document is unreachable. The orchestrator escalates to a human queue with the partial result attached, since no amount of retry budget fixes a missing source.

Without the envelope, the same run most likely produces an apology and a partial dump. The orchestrator either retries everything (paying for work that already succeeded) or discards everything (losing 28 records' worth of output). The envelope's value is not elegance, it is measured in avoided re-computation. Note also that coordination has its own price: a three-agent pipeline consumed roughly 29,000 tokens versus 10,000 for an equivalent single-agent approach, per Beam AI's production patterns writeup. A split is a tool to use when coverage is low, not a default.

Making retry_hint useful without trusting it

The subagent sees the failure from the inside, so it often knows whether the remaining work is parallelizable, whether a source was flaky, or whether the scope was simply too wide. Capture that in retry_hint, then treat it as advisory. Three rules keep it honest:

  • Bound the vocabulary. suggested_action takes values from the same table actions: RESUME, SPLIT, NARROW, ESCALATE, FAIL. If the subagent proposes RESUME on a CONTEXT_OVERFLOW, the orchestrator's table overrules it, because fresh context is the documented reason recursive decomposition helps, as Gandhi et al. describe.
  • Ignore proposed budgets. Let the orchestrator, not the worker, own the multiplier. A worker that asks for ten times its budget is asking you to spend money on its own optimism.
  • Log the disagreement. When the hint and the decision table differ, record both. Over a few weeks those mismatches tell you which reason_codes your table gets wrong, which is cheaper than tuning prompts.

Keep schema violations as contract failures and send them to a repair path, the same way you would treat any malformed handoff MLflow. And correlate traces across the orchestrator and its workers, since a cap event is only debuggable if you can see the budget at the moment it fired; practical guidance on agent observability and failure handling covers the trace side of this the JetBrains single versus multi-agent guide.

Worked against real traffic, the envelope also gives you a migration path. Start with a single agent and one cap, wrap it in the envelope, and add reason codes only when you observe the failure. Microsoft's guidance to use the lowest level of complexity that meets requirements applies to your error taxonomy as much as to your architecture Microsoft.

If you are running several subagents behind one orchestrator and want one key for text, image and embedding models, Quanta Inference (https://quantaintelligence.ai/inference) keeps the routing and billing side to one interface while your code owns the envelope contract.

FAQ

FAQ

Why not just ask the model to return an error message in prose?

Because the orchestrator cannot branch on prose without writing brittle parsing rules, and small ambiguities compound across a chain of handoffs MLflow. A typed envelope turns the failure into a value the code already knows how to handle. It also lets you validate the partial result before it enters shared state.

Should the partial result be validated even when the subagent says it is fine?

Yes. A schema-valid output can still violate domain rules, and providers can produce structurally compliant JSON that is semantically wrong TrueFoundry. Constrained decoding nearly eliminates syntax errors but structural and semantic errors persist Song et al.. Treat the subagent's own schema_ok flag as a claim to check, not a fact to trust.

What coverage threshold should trigger a split instead of a retry?

Any threshold you pick is a policy, not a law, but 0.8 for budget-caused caps is a reasonable starting point, because high coverage means more of the same work will finish the job. Below that, the evidence points at scope rather than budget, and decomposition is the documented strength of subagent architectures LangChain. Measure your own mismatch rate over a week before you tune it.

How many reason codes should the closed set have?

Start with the caps you actually observe and add codes only when a new failure needs a different recovery action. A code that maps to the same action as another code is noise. Six or so values, each with a distinct row in the decision table, is enough for most orchestrator-worker systems Arize's orchestrator-worker comparison.

Does the envelope replace tracing and budgets?

No, it complements them. The envelope is a per-run return contract; tracing shows you the budget that fired and the steps that ran; budgeting decides what the caps are. Multi-agent workflows need typed interfaces, runtime orchestration decisions and centralized observability at once, and skipping any one of those produces drift or silent failures that are hard to debug after the fact MLflow.

Sources

Sources

  1. Choosing the Right Multi-Agent Architecture - LangChain
  2. One post tagged with "agent orchestration patterns"
  3. How Anthropic Built a Multi-Agent Research System
  4. Multi-Agent Cost Compounding: Why 3 Agents Cost 10x
  5. The guide to structured outputs and function calling with ...
  6. LLM Structured Outputs with JSON Schema
  7. Partially structured output? Free text output, but force ...
  8. Empirical Study for Structured Output Control in LLMs for ...
  9. AI Agent Orchestration Patterns - Azure Architecture Center
  10. 6 Multi-Agent Orchestration Patterns for Production (2026) - Beam AI
  11. Recursive Agent Optimization
  12. Single Agent vs Multi-Agent Systems: When to Use Each - JetBrains
  13. Orchestrator-Worker Agents: Comparing Agent Frameworks
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