Skip to content

run() abort with conversationId leaves orphan function_call items in the conversation store #1190

@BrodaNoel

Description

@BrodaNoel

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions