Please read this first
- Have you read the docs? Agents SDK docs Yes
- Have you searched for related issues? Others may have faced similar issues. Yes
Describe the bug
When a streamed run() is aborted via its signal after the model has emitted one or more function_calls but before the SDK executes the tools and sends the corresponding function_call_outputs, those calls stay in the server-side conversation store without outputs. Any later run() against the same conversationId fails with:
400 No tool output found for function call call_XXX
Our use case
We run an SSE endpoint that pipes run(agent, input, { stream: true, conversationId, signal: controller.signal }) to the client. We wire controller.abort() to req.on('aborted') / req.on('close') to stop the run when the user closes the tab, refreshes, or loses the network. Whenever that happens mid tool-call, the conversation becomes unusable and we have to reconcile orphan items manually via the Conversations API.
Reproduction
const controller = new AbortController();
const stream = await run(agent, input, {
stream: true,
conversationId,
signal: controller.signal,
});
for await (const e of stream) { /* consume */ }
Call controller.abort() while the model is streaming a function_call, then start a new run() against the same conversationId.
Root cause
In @openai/agents-core, run.js around the streamed response loop:
catch (error) {
if (isAbortError(error)) {
if (sentInputToModel) markInputOnce();
await awaitGuardrailsAndPersistInput();
return;
}
throw error;
}
The SDK returns without running resolveTurnAfterModelResponse / executeFunctionToolCalls, so no function_call_outputs are generated. With conversationId, OpenAI has already persisted the function_calls server-side as they streamed, and no cleanup is attempted.
(Tools that throw are handled correctly by defaultToolErrorFunction; corruption is specific to aborts that interrupt the turn after the model emitted the call but before tool execution.)
Suggested fix
On AbortError, before returning, emit a synthetic function_call_output (e.g. "" or "aborted") for each function_call the model already streamed in the current turn, so the conversation store stays consistent and reusable.
Debug information
- Agents SDK version: "@openai/agents": "^0.8.3",
- Runtime environment (e.g.
Node.js 24.2.0)
Repro steps
it's super hard to get this code example. I have tried a lot of times, but it's super time-sensitive. The AbortController should call an abort riiiight in exact proper time.
Expected behavior
The conversation should not be corrupted.
Please read this first
Describe the bug
When a streamed
run()is aborted via itssignalafter the model has emitted one or morefunction_calls but before the SDK executes the tools and sends the correspondingfunction_call_outputs, those calls stay in the server-side conversation store without outputs. Any laterrun()against the sameconversationIdfails with:Our use case
We run an SSE endpoint that pipes
run(agent, input, { stream: true, conversationId, signal: controller.signal })to the client. We wirecontroller.abort()toreq.on('aborted')/req.on('close')to stop the run when the user closes the tab, refreshes, or loses the network. Whenever that happens mid tool-call, the conversation becomes unusable and we have to reconcile orphan items manually via the Conversations API.Reproduction
Call
controller.abort()while the model is streaming afunction_call, then start a newrun()against the sameconversationId.Root cause
In
@openai/agents-core,run.jsaround the streamed response loop:The SDK returns without running
resolveTurnAfterModelResponse/executeFunctionToolCalls, so nofunction_call_outputs are generated. WithconversationId, OpenAI has already persisted thefunction_calls server-side as they streamed, and no cleanup is attempted.(Tools that
throware handled correctly bydefaultToolErrorFunction; corruption is specific to aborts that interrupt the turn after the model emitted the call but before tool execution.)Suggested fix
On
AbortError, before returning, emit a syntheticfunction_call_output(e.g.""or"aborted") for eachfunction_callthe model already streamed in the current turn, so the conversation store stays consistent and reusable.Debug information
Node.js 24.2.0)Repro steps
it's super hard to get this code example. I have tried a lot of times, but it's super time-sensitive. The AbortController should call an abort riiiight in exact proper time.
Expected behavior
The conversation should not be corrupted.