Disclosure: I work on MemorySync (I'm Mohammed, the founder). I drafted this page with AI assistance and checked it against the source thread and the official docs listed under Sources on Oct 28, 2026. I read the issue, the fix PR and the release notes; I didn't run the reproducer for this page.
The question
openai/openai-agents-python#4827 reports that after an approved tool call is resumed, the next Runner.run(..., session=session) fails, and keeps failing:
openai.BadRequestError: Error code: 400 - No tool call found for function call
output with call_id call_ORPHAN
The issue says the Session ends up holding a function_call_output whose function_call was never written, and since that orphan is stored durably, "every subsequent turn fails the same way."
Status (re-checked Oct 29, 2026): closed as completed on Oct 6, 2026, by PR #4828, "fix(sessions): persist deferred approval history across resumes". The fix is listed in the v0.24.0 release notes (Oct 15, 2026). The latest openai-agents on PyPI is 0.24.0.
Skip the memory plumbing around this fix. Once the fix below is in, the MemorySync OpenAI Agents SDK guide shows how to add per-user memory with MemorySync. MemorySync doesn't fix this error itself. When you're ready, sign up and try MemorySync. Founding offer: use code FOUNDING30 for 30% off Starter for your first 3 months (Settings → Billing).
When it happens
Per the issue, all of these together, on versions since the deferral change shipped in 0.22.0:
- the agent has
output_guardrails, and -
tool_use_behavioris not the default"run_llm_again"(the reproducer usesStopAtTools), and - a
needs_approval=Truetool parks the run, and - after approval, the resumed tool is not a terminal tool, so the run continues.
The interrupted turn's items (the function_call and the approval item) are held back at park time. On that resume path nothing wrote them, so only the tool output landed in the Session. One commenter reproduced the orphan offline on 0.22.3 with ScriptedModel and SQLiteSession.
Fix
- Upgrade to openai-agents 0.24.0 or later. That's the release that contains #4828.
-
Repair sessions already poisoned. Upgrading doesn't rewrite stored history, so an orphan written before the upgrade still makes every request fail. Options, using the SDK's Session methods:
- start a new
session_idfor that conversation, or -
clear_session()if losing that conversation is acceptable, or - if the orphan is the last item,
pop_item()removes it.
- start a new
-
Check before you run (optional). Every
function_call_outputinawait session.get_items()should have an earlierfunction_callwith the samecall_id. If not, don't send that history.
How to tell it's this bug
- The approval resume succeeds, and the failure shows up on the next run.
- The failing
call_idis the one from the approval-gated tool. - A brand-new session works; the old one fails on every turn.
The same 400 shows up in other stacks whenever a tool output is replayed without its call (for example a stream aborted mid-turn), so check the session items rather than assuming it's this exact path.
Common questions
Is this a bug in SQLiteSession?
No. The reproducer uses SQLiteSession, but the missing write was in the runner's resume path, so any Session backend received the same incomplete items.
Do I need output guardrails to hit it?
Per the issue, yes: the deferral only applied with output guardrails plus a non-default tool_use_behavior.
Skip the plumbing with MemorySync
It doesn't fix this. The MemorySync OpenAI Agents SDK guide documents MemorySyncSession, which implements the SDK's Session protocol (get_items, add_items, pop_item, clear_session) and returns items "exactly as the SDK wrote them". So it stores whatever the runner hands it, and you'd still upgrade the SDK. What it adds is server-side conversation history keyed by session_id with a required user_id, and user messages sent to fact extraction so durable facts become that user's long-term memories across sessions. Read the guide first. To see save and recall in your browser, try the MemorySync playground.
From the guide:
pip install openai-agents-memorysync openai-agents
from agents import Agent, Runner
from openai_agents_memorysync import MemorySyncSession
agent = Agent(
name="Assistant",
instructions="You are a helpful assistant.",
)
session = MemorySyncSession(
"thread-42", # the conversation
user_id="customer-7", # the end user it belongs to — required
)
# First conversation
await Runner.run(agent, "I'm vegetarian and I fly aisle.", session=session)
# Any later run — same session id, any process, any deploy
result = await Runner.run(
agent, "Book my trip: flight plus a dinner spot.", session=session
)
# The model saw the full prior history — no manual .to_input_list() plumbing.
Snippet adapted from the MemorySync OpenAI Agents SDK guide; I haven't run it for this page.
Sources
- openai/openai-agents-python#4827 (closed 2026-10-06, completed; re-checked 2026-10-29)
- openai/openai-agents-python#4828 (merged 2026-10-06)
- openai-agents-python v0.24.0 release notes (2026-10-15)
- MemorySync docs: docs.memorysync.io/guides/openai-agents
Next step: read the MemorySync OpenAI Agents SDK guide, then sign up and try MemorySync. Founding offer: use code FOUNDING30 for 30% off Starter for your first 3 months (Settings → Billing).
Top comments (0)