Responses API + TypeScript SDK: “No tool output found for function call” despite correct echo and store: true

Hello everyone,

I’m encountering an issue with the Responses API in the TypeScript SDK when using function calling. The error I see is:

BadRequestError: 400 No tool output found for function call call_<ID>.

This is not a duplicate‑ID problem. I can confirm via the OpenAI history endpoint that both the function_call event and its corresponding function_call_output are present:

{
  "data": [
    {
      "id": "…",
      "type": "function_call",
      "status": "completed",
      "call_id": "call_ABC"
    },
    {
      "id": "…",
      "type": "function_call_output",
      "call_id": "call_ABC",
      "output": "{\"success\":true}"
    },
    {
      "id": "msg_…",
      "type": "message",
      "content": [{"text":"Parameter applied"}]
    }
  ]
}

But immediately after sending a new user message in the same session (using previous_response_id), the SDK still throws:

No tool output found for function call call_ABC.

Here’s my code example:

import OpenAI from "openai";

interface ChangeParamArgs {
    param: string;
    value: string;
}

async function changeParam({ param, value }: ChangeParamArgs): Promise<{ success: boolean }> {
    console.log(`Changing parameter ${param} to ${value}`);
    return { success: true };
}

async function main() {
    const client = new OpenAI({ apiKey: "**key**" });

    const resp1 = await client.responses.create({
        model: "gpt-4o",
        store: true,
        instructions: "you are a helpful assistant that can change parameters",
        tools: [
            {
                type: "function",
                name: "change_param",
                description: "Use this tool to change a parameter",
                strict: false,
                parameters: {
                    type: "object",
                    properties: {
                        value: {
                            type: "string",
                            description: "The new value for the parameter"
                        },
                        param: {
                            type: "string",
                            description: "The parameter to change"
                        }
                    },
                    required: [
                        "value",
                        "param"
                    ]
                }
            }
        ],
        input: [
            { role: "user", content: "change param mode from slow to fast" }
        ]
    });

    console.log("Assistant:", JSON.stringify(resp1.output[0], null, 2))

    const callEvent = resp1.output.find(o => o.type === "function_call");
    if (!callEvent) {
        throw new Error("no function call event");
    }

    const args: ChangeParamArgs = JSON.parse(callEvent.arguments);
    const result = await changeParam(args);

    const resp2 = await client.responses.create({
        model: "gpt-4o",
        previous_response_id: resp1.id,
        store: true,
        input: [
            {
                type: "function_call",
                call_id: callEvent.call_id,
                name: callEvent.name,
                arguments: callEvent.arguments
            },
            {
                type: "function_call_output",
                call_id: callEvent.call_id,
                output: JSON.stringify(result)
            }
        ]
    });

    console.log("Assistant:", JSON.stringify(resp2.output[0], null, 2))

    const resp3 = await client.responses.create({
        model: "gpt-4o",
        previous_response_id: resp1.id,
        store: true,
        input: [
            { role: "user", content: "what is the mode now?" }
        ]
    });

   // error here

    console.log("Assistant:", JSON.stringify(resp3.output[0], null, 2))
}

main().catch(console.error);

Questions:

  1. Is this a bug in the Responses API or the TS SDK?
  2. Can anyone share a working TS/Responses API example where the call/output chain truly persists across messages?

Thank you for any insights!

I got this resolved by passing very specific messages (with these parameters).

messages.append(
            {
                "type": "function_call",
                "call_id": tool_call.id,
                "name": tool_call.name,
                "arguments": tool_call.arguments,
            }
        )
messages.append(
            {
                "type": "function_call_output",
                "call_id": tool_call.id,
                "output": result.value,
            }
        )

How do you further utilise this array?

If you collect all the messages in it, then the concept of using response id in the next queries makes no sense.

Hi Oleksandr,
I have successfully solved that issue by using the method that Rodrigo mentioned.
You can get the arguments, output, and call_id by retriving the previous response.