AI agents

When a Subagent Returns Partial Work: Accept or Fail Loudly

Antonio Evans8 min read

A subagent hands a reviewer a stack of papers with some pages blank and a sticky note listing what is missing, while a red error card shows the alternative outcome.
Key points

Key points

  1. 1Accept partial work only when the missing steps are off the critical path for the caller's next action, the partial output can be verified quickly, and the return lists exactly what was and was not done.
  2. 2A subagent that runs out of budget and returns a silent empty success is more dangerous than one that errors, because the caller has no signal to retry or escalate.
  3. 3A fixed return schema with an explicit status field turns the accept-or-fail decision into a check the orchestrator can make without reading free text.
  4. 4Whatever the subagent decides, the reviewer needs the same four facts: what failed, what was attempted, what was gathered, and what to try next.

Why the split decision is hard

A subagent runs in its own context window. It does not see the parent conversation, and the briefing it receives is whatever the dispatcher wrote in the delegation prompt. Everything the child learns has to be compressed into a return message, because the parent reads only the summary, not the trail of tool calls that produced it (the delegation guide). That design is the point: noisy work stays out of the main thread, and only the conclusion flows back. But it also means the return message is the only channel, and if the child says the wrong thing about its own state, the parent has nothing else to check.

Add a nested depth of two or three and the problem compounds. A mid-tier agent that returns a half answer does so upward into another agent that may itself be close to its own limit. Anthropic's engineering team measured its multi-agent research system using roughly 15 times the tokens of a chat interaction, and reported that token usage alone explained 80 percent of performance variance on one benchmark (the cost analysis). Depth is a budget you spend, not headroom you fill (the depth probe).

The failure modes are well documented. One reported Claude Code run had a single delegation for "research Venmo integration options" fan out into more than 48 background agents, most doing redundant searches, burning over 1.5 million tokens; the useful research was finished within the first three or four agents (issue 68110). A separate report describes the orchestrator receiving only a generic failure string when a subagent dies, then either re-dispatching the identical task or confidently fabricating what the child "would have found" (issue 25818). Both of those are return-channel problems, not model problems.

The decision rule

Accept partial work when all three conditions hold.

  1. The missing steps are not on the critical path for the caller's next action. If the parent needs field X to write a record, and the child never reached the table containing X, a partial return is worthless to the parent even if it contains twenty other tables. If the parent is compiling a research brief and the child covered four of five subtopics, the gap is survivable.
  2. The partial output can be checked mechanically or with a quick human glance. A diff that passes tests, a list of file paths that exist, a table of numbers that reconciles: these can be confirmed. A judge of a prose quality cannot. When verification costs more than re-running the task, accept nothing.
  3. The return carries a complete list of what was and was not done. Not a hedge ("I mostly finished"), but an inventory with identifiers.

If any one fails, fail loudly. Loudly means an error object that names the budget limit, the step where it was hit, and any results already gathered.

The reason to be strict about condition three is the observability gap. A subagent's failure typically reaches the orchestrator as a thin string, with no record of what it accomplished, why it failed, how far it got, or whether a retry will hit the same wall (issue 25818). If the schema itself does not force a complete inventory, the parent will guess, and guessing is where fabricated results come from.

There is a fourth, subtler test: would the reviewer act on this return without knowing it was partial? If a reader could mistake the output for a complete answer, the child must mark it. Coverage annotations are the cheapest fix: a synthesis that says "section on brick costs is limited because the merchant price list was unavailable" is far better than a silent omission (the error propagation guide).

A return schema that makes the choice explicit

Encode the decision in the contract so that neither the child nor the parent has to interpret prose. A minimal schema carries status, reason, the budget limit that was hit, an inventory, the payload, and the suggested next step.

{
  "status": "partial",
  "reason": "budget_exhausted",
  "limit": { "type": "output_tokens", "value": 40000 },
  "stopped_at_step": "cross-check prices against last quarter invoices",
  "completed_steps": [
    "pulled all 12 supplier price rows",
    "normalized currency for 11 of 12 (missing GBP rate)"
  ],
  "missing_steps": [
    "cross-check against invoices",
    "flag outliers above 8 percent variance"
  ],
  "payload": { "prices": [ "..." ] },
  "verification": { "type": "row_count", "expected": 12, "found": 11 },
  "suggestions": [
    "rerun only the invoice cross-check with a fresh 20k budget",
    "accept the 11 rows and mark the GBP row unknown"
  ]
}

Three details matter more than the field names.

Use three statuses, not two. success, partial, and failed are distinct. Collapsing partial into success is silent suppression, the worst anti-pattern, because the coordinator believes the search ran and found nothing and will not retry (the error propagation guide). Collapsing it into failed is the other extreme: one slow worker kills a pipeline and throws away four finished lookups.

Distinguish access failure from a valid empty result. A tool that times out did not reach the data. A query that returned zero rows did reach it. Concrete error objects should carry a retryable boolean and the timeout or permission message, separate from a successful result with an empty list. Conflating the two either triggers pointless retries or skips retries that were needed.

Make the schema fail closed. A structured delegation that returns a non-conforming payload must not report itself as completed. One agent framework shipped exactly this bug: delegate_task could report status = "completed" alongside schema_valid = false, so downstream automation treated an invalid structured response as a success and gated code review on it (the Hermes issue). If the caller asked for a schema, an invalid payload is a failure, whatever the status string says.

OutcomeWhat the child returnsWhat the parent should doWhat the reviewer sees
Full successstatus: success, payload, verification evidenceConsume directlyFinished result with evidence
Partial, off critical pathstatus: partial, inventory, payload, suggestionsUse the payload, schedule a scoped follow-upResult plus an explicit gap note
Partial, on critical pathstatus: failed, reason critical_step_missingDo not consume; reshape the task or escalateError naming the missing step
Budget exhausted, nothing usablestatus: failed, reason budget_exhausted, limit, stopped_at_stepRaise the limit or narrow the task; never re-dispatch verbatimError with the limit and the last step
Schema violationstatus: failed, schema_valid: falseFail the gateError, not a passing result

The distinction between rows two and three is the heart of the rule, and it is the one most teams get wrong. The child often cannot know which steps are on the parent's critical path, so the delegation prompt must state the caller's next action. "I need the supplier list by country" tells the child that a missing country is fatal. "I need whatever you can find on supplier pricing" tells it that a gap is tolerable. One sentence in the briefing removes most ambiguous partials.

What the reviewer actually sees

The two outcomes read very differently downstream, and the gap explains why "fail loudly" usually wins ties.

A partial return hands a human a judgment task. The reviewer must read the payload, read the inventory, decide whether the gap matters for the decision at hand, and then decide whether to trust the verification evidence. That is real work, and it is exactly the kind of work that oversight design keeps getting wrong. Industry guidelines tell users to audit and double-check outputs, but the mechanisms users would need to do this effectively are largely absent, and extended reliance on automation degrades the judgment oversight depends on (the oversight paper). A partial return with no coverage annotation quietly shifts workload onto the reviewer and offers no scaffold for it.

A loud failure hands the reviewer a simpler task: pick one of a short list of recovery moves, each of which the error names. Retry with a narrower scope, raise the budget, fix the precondition, or escalate. That is recoverable in a single exchange. Structured error context should carry four elements for exactly this reason: the failure type, what was attempted, partial results gathered before failure, and alternative approaches (the error propagation guide). The bricklaying example in that guide is a good scale model: five subagents look up brick prices, mortar and sand prices, the calendar, past jobs and order history; the brick price subagent times out; the coordinator uses the three prices it got and retries the rest rather than either failing the quote or shipping it without a brick cost.

Audit evidence pushes the same way. An auditor sampling a sensitive action wants to see the policy that applied, the approver, the timestamp, and the outcome of the side effect, tied together by correlation IDs, not just a tool trace (the audit trail guide). A partial return whose gap is invisible in the record is worse than no record, because it looks like evidence. A failure with a reason code and a correlation ID is at least honest.

For a quick reviewer-facing checklist:

  • Does the return state the caller's next action was unblocked or blocked?
  • Is there a count or checksum that can be confirmed in seconds?
  • Does every "done" item have an identifier (path, row, ID) a person can open?
  • Does every "not done" item name the step, not just express regret?
  • Could a reader mistake this output for a complete answer? If yes, the annotation is missing.

If any answer is no, the honest move is to fail and say why.

Designing the budget so the choice is rare

Most partial returns exist because nobody told the child where the edge was. A subagent with no budget awareness consumes context until it crashes, and the parent then has no idea how close it got (issue 25817 referenced alongside 25818). Frameworks are moving toward explicit budgets: one harness exposes a shared budget object where hitting the ceiling blocks new spawns while agents already running are allowed to finish, and skipped items come back with a budget_exceeded status instead of vanishing (the gptme subagent docs). Claude Code added a --max-budget-usd flag and had to fix it so that reaching the cap actually halts background subagents rather than letting them spend past the limit (the guardrails summary).

Three practices make the split decision uncommon:

Stash checkpoints before the last expensive step. If the child writes its findings incrementally, then running out of budget produces a real partial rather than a lose-everything crash. The parent gets usable state and a clean gap.

Give every child a reserve for the return. A subagent that spends its entire budget on tool calls has nothing left to write the summary. Reserve some fraction for the final message and the inventory.

State the caller's next action in the delegation prompt. This is the single highest-leverage change. The child cannot rank its own subtasks against an invisible critical path, and one line of context fixes that.

Teams running many delegations across providers face a related cost problem: the same context gets re-billed at every hop, and budgets set per agent miss the interactions between them (the cost analysis). A single API for every model type, with prepaid pricing in dollars, makes the budget side of planning simpler and keeps the ceiling legible: Quanta Inference (https://quantaintelligence.ai/inference) exposes text, image, video, audio and embedding models through one OpenAI-compatible key.

Testing the return path

The return contract is code, so test it like code. A small fixture suite catches most regressions:

def test_budget_exhaustion_with_gap_off_critical_path():
    result = run_child(task="list 12 suppliers by country", budget=2000,
                       caller_next_action="draft a supplier email")
    assert result["status"] == "partial"
    assert result["reason"] == "budget_exhausted"
    assert result["verification"]["found"] < result["verification"]["expected"]
    assert "missing_steps" in result and result["missing_steps"]

def test_budget_exhaustion_on_critical_path_fails():
    result = run_child(task="fetch GBP exchange rate", budget=10,
                       caller_next_action="price the GBP order")
    assert result["status"] == "failed"
    assert result["stopped_at_step"]

def test_schema_violation_never_reports_success():
    result = run_child(task="review this diff", output_schema=REVIEW_SCHEMA,
                       force_invalid_response=True)
    assert result["status"] == "failed"
    assert result["schema_valid"] is False

def test_empty_result_is_not_an_access_failure():
    result = run_child(task="find orders placed tomorrow")
    assert result["status"] == "success"
    assert result["payload"] == []
    assert result.get("retryable", False) is False

Four tests, and each one corresponds to a failure mode that has been observed in the wild: runaway delegation, blind retry, schema-invalid success, and conflated empty results.

If your review of subagent returns turns into a larger program of work, the reviewers and auditors who own the acceptance decision are the people Quanta Experts (https://quantaintelligence.ai/agents) is built for: each expert reads the context, does the work, and hands back a finished deliverable rather than raw tool output.

FAQ

FAQ

When should a subagent return partial work instead of failing?

Accept partial work only when the missing steps are not on the critical path for the caller's next action, the output can be verified mechanically or with a quick human check, and the return carries a complete list of what was and was not done. If any of those fails, return an error that names the budget limit and the step where it was hit. The rule exists because a silent or ambiguous partial looks like a finished answer to a reviewer.

Why is a silent empty success worse than an error?

A silent empty success tells the caller that the work ran and found nothing, so nobody retries and the gap disappears into the output. An error with a reason code tells the caller exactly what to do next. The error propagation guide names silent suppression of empty results as the worst anti-pattern precisely because it is invisible (guide).

What fields should the return object carry?

At minimum: status (success, partial, or failed), reason, the budget limit that was hit, the step where it stopped, an inventory of completed and missing steps, the payload gathered so far, verification evidence, and suggested next actions. If a schema was requested, include a schema validation flag and fail closed when it is invalid (schema example).

How should the parent handle a subagent that fails?

Match the recovery to the failure type: budget exhaustion means narrow the task or raise the limit, never re-dispatch the same prompt; a crash gets one retry with the same prompt; a max-turns stop returns partial results and asks whether to continue; a tool error gets its precondition fixed first. The one rule with no exceptions is never fabricate what the subagent would have found (issue 25818).

Does nesting depth change the rule?

It raises the stakes but not the rule. At each level, a partial return is compressed again on the way up, and a mid-tier agent nearing its own limit has less room to reason about the gap. Depth is a budget you spend rather than headroom you fill (the depth probe), so state the caller's next action earlier and reserve budget for the return message at every level.

Sources

Sources

  1. Claude Code Subagents and Multi-Agent Orchestration ...
  2. Multi-Agent Cost Compounding: Why 3 Agents Cost 10x
  3. The Changelog Says 5 Levels. My Probe Went 9 Deep. Inside ...
  4. General-purpose sub-agents recursively spawn ...
  5. Orchestrator has no diagnostic context when subagent fails ...
  6. 5.3, Error Propagation in Multi-Agent Systems | AI Cert Guides
  7. delegate_task can return completed when output_schema ...
  8. AI Agents Push Humans Out of the Loop - arXiv
  9. AI Agent Audit Trail: Proving What an Agent Did and Why
  10. Subagent
  11. Claude Code Put Guardrails on Its Own Agent Fleets
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