Issue with New Responses API - “400 No tool call found for function call output with call_id”

I’m encountering an issue with the OpenAI Responses API when handling function call outputs. The tool call appears to be correctly initiated and processed, but OpenAI returns a 400 No tool call found for function call output error despite the call_id matching the function call.

{
  "type": "response.output_item.added",
  "output_index": 0,
  "item": {
    "type": "function_call",
    "id": "fc_67d2e3ccdee0819188bf879ce5393b760b3361b29ca82d",
    "call_id": "call_Co8dkB8h7NcTYq9dGnol",
    "name": "add",
    "arguments": "",
    "status": "in_progress"
  }
}
[
  {
    "type": "function_call_output",
    "call_id": "call_Co8dkB8h7NcTYq9dGnol",
    "output": "{\"test\":\"test\"}"
  }
]

this is how I submit the toolcall. But its giving me status
Error: 400 No tool call found for function call output with call_id call_Co8dkB8h7NcTYq9dGnol.

How can i fix this?

1 Like

Welcome to the dev community @vijayvinayaraj2000

Are you including the original tool call in the conversation history when submitting the tool call output?

2 Likes

I am seeing the same.

I am returning the OpenAI object that is expected (from openai/types/responses/response_input_param.py) but have also tried json with the same values.

FunctionCallOutput(
   id=tool_call.id,
   call_id=tool_call.call_id,
   type="function_call_output",
   output=result.model_dump_json(),
   status="completed",
)

I am making a second request with that object in the input with the previous message id set correctly to the response that contained the tool request.

And to answer @sps, I am not including the original tool call as my expectation for the responses api is that it would be unnecessary given the previous message history. Regardless, the error itself would indicate that there is an expectation of a tool call being made that it is not seeing the output for, not that we returned output that was unexpected (eg, it knows of the requested tool call).

I’ve tried multiple models (4o latest, gpt-4o-2024-08-06, o1) and I have verified that the id and call_id I am returning match the values originally received/requested.

It’s necessary to include the original tool call as well as the results as input. Please see step 4: https://platform.openai.com/docs/guides/function-calling?api-mode=responses#function-calling-steps

3 Likes

The actual function call ID sent by an assistant is not enforced, to where you have to absolutely use what the model sent you. You can make up your own.

The issue and primary requirement is that a function_call must be immediately followed by a function_call_output, both with call_id. The purpose of call_id is to pair the return to the calling function and its arguments in the case of parallel tool calls.

The return is not the whole assistant response, just the function call now as a separate input list item - and the function execution result together, with their matching IDs.

You have an AI that makes a tool call, which I can show from just getting the first function directly (instead of by streamed event parser):

>>print(json.dumps(events[-1:][0].response.output[0].model_dump(), indent=2))

{
  "id": "fc_67d484e56338819296d2cd4d99768a3a0e9fbfd54e8a25c2",
  "arguments": "{\"us_city\":\"Toledo, OH\"}",
  "call_id": "call_xyz1234",
  "name": "get_current_us_weather",
  "type": "function_call",
  "status": "completed"
}

Then your function reuses the call id:

    {
        "type": "function_call_output",
        "call_id": "call_xyz1234",
        "output": "Toledo, OH - Current conditions: 62F, partly cloudy",
    }

Here is how to actually be returning a function call to the Responses API in input with stateless messages.

First, I give you an entire API request JSON body (with “input”), stripped right from the network call of the Prompts Playground when it was returning a function output:

{
  "model": "gpt-4o",
  "input": [
    {
      "role": "system",
      "content": [
        {
          "type": "input_text",
          "text": "You are WeatherPal, a virtual assistant acclaimed for your meteorology expertise and your ability to provide accurate weather conditions across the United States.\nCurrent time of latest input: 2025-03-14T17:22:00Z"
        }
      ]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "input_text",
          "text": "Toledo Ohio looking hot out right now?"
        }
      ]
    },
    {
      "type": "function_call",
      "call_id": "call_qCYF1LIA9qAK0Nvp24dMqNUP",
      "name": "get_current_us_weather",
      "arguments": "{\"us_city\":\"Toledo\"}"
    },
    {
      "type": "function_call_output",
      "call_id": "call_qCYF1LIA9qAK0Nvp24dMqNUP",
      "output": "Toledo, OH - Current conditions: 62F, partly cloudy"
    }
  ],
  "tools": [
    {
      "type": "function",
      "name": "get_current_us_weather",
      "description": "Retrieves the current weather for a specified US city",
      "parameters": {
        "type": "object",
        "required": [
          "us_city"
        ],
        "properties": {
          "us_city": {
            "type": "string",
            "description": "The name of the US city for which to retrieve the current weather"
          }
        },
        "additionalProperties": false
      },
      "strict": true
    },
    {
      "type": "function",
      "name": "get_usa_city_forecast",
      "description": "Function for 5-day forecast retrieval.",
      "parameters": {
        "type": "object",
        "required": [
          "us_city"
        ],
        "properties": {
          "us_city": {
            "type": "string",
            "description": "Major city name, state abbreviation (e.g. Miami, FL)."
          }
        },
        "additionalProperties": false
      },
      "strict": true
    }
  ],
  "text": {
    "format": {
      "type": "text"
    }
  },
  "temperature": 1,
  "top_p": 1,
  "parallel_tool_calls": true,
  "reasoning": {},
  "stream": true,
  "max_output_tokens": 2048,
  "store": true
}

Then let’s break it down, writing Python to make the complete API call (but not demonstrating parsing the assistant message that had the first function call)

Weather function? Why not two of them…with strict structured outputs

from openai import OpenAI

MODEL = "gpt-4o"
TOOLS = [
    {
        "type": "function",
        "name": "get_current_us_weather",
        "description": "Retrieves the current weather for a specified US city",
        "parameters": {
            "type": "object",
            "required": ["us_city"],
            "properties": {
                "us_city": {
                    "type": "string",
                    "description": (
                        "Major city name, state abbreviation (e.g. Miami, FL)."
                    ),
                }
            },
            "additionalProperties": False,
        },
        "strict": True,
    },
    {
        "type": "function",
        "name": "get_usa_city_forecast",
        "description": "Function for 5-day forecast retrieval.",
        "parameters": {
            "type": "object",
            "required": ["us_city"],
            "properties": {
                "us_city": {
                    "type": "string",
                    "description": (
                        "Major city name, state abbreviation (e.g. Miami, FL)."
                    ),
                }
            },
            "additionalProperties": False,
        },
        "strict": True,
    },
]

The function specification with your functions is indeed a type of tool. It is placed in the AI context with other tools that tell the AI how to send to them, but functions uses a post-trained namespace format.

The API request input context that sends function execution results

We’re sending a system (or developer) message, and the user question. Then the assistant-produced function call, and a function return. You’d also include “assistant” role “output_text” if the AI wrote something along with its tool call (which is different than the single assistant role message of Chat Completions being fed back with tool_calls inside).

INPUT = [
    {
        "role": "system",
        "content": [
            {
                "type": "input_text",
                "text": (
                    "You are WeatherPal, a virtual assistant acclaimed "
                    "for your meteorology expertise and your ability to "
                    "provide accurate weather conditions across the United States.\n"
                    "Current time of latest input: 2025-03-14T17:22:00Z"
                ),
            }
        ],
    },
    {
        "role": "user",
        "content": [
            {
                "type": "input_text",
                "text": "Toledo Ohio looking hot out right now?",
            }
        ],
    },
    {
        "type": "function_call",
        "call_id": "call_xyz1234",
        "name": "get_current_us_weather",
        "arguments": "{\"us_city\":\"Toledo, OH\"}",
    },
    {
        "type": "function_call_output",
        "call_id": "call_xyz1234",
        "output": "Toledo, OH - Current conditions: 62F, partly cloudy",
    },
]

The API call, by OpenAI’s Python library module SDK

# Create an OpenAI client instance, which uses OPENAI_API_KEY of environment
client = OpenAI()

response = client.responses.create(
    model=MODEL, input=INPUT,
    tools=TOOLS, tool_choice="auto", parallel_tool_calls=True,
    temperature=1, top_p=0.5, max_output_tokens=2048,
    stream=False, store=False,
)

# Output the non-stream response
print(response.output[0].content[0].text)

(This print command only expects output text, while the AI could be invoking another function call for you to capture.)

Put it all together, and the AI will happily answer about what your function had returned:

It's currently 62°F and partly cloudy in Toledo, Ohio. It doesn't seem too hot right now!

(updated to focus on the prominence of “functions” vs “tools” in the usage of Responses API endpoint)

Appreciate the responses (and the link to the function calling docs updated for Responses, I had somehow been unable to find that).

“role”: “system” can be huge!
Can’t we just provide previous_response_id ?

This was extremely helpful (as always) - trying to wrap my head around migrating from Assistants to Responses - I have a question, finally got it all to work - but is this approach the right way?

Response #1

“hey help me out with this”
(responses returns a tool request)


I send this to the backend to get the results of the tool and the stream the next response:

Response #2

here’s your answer (from the tool call)


So in Logs I have one Response with no real output, and then a second Response with the answer on it (using previous_response_id)

Something tells me that’s not right - plus on the front end its a little wonky to have a response with no output … am I missing something here?

I’ll show how the playground implements and demonstrates my new (imaginary) function

API call 1:

The output was a tool call with no content.

API call 2: furnishing a tool return

The output was content (with no tool call).

API call 3: using chat history

API call 3: alternate, because you broke chat history

So: the assistant tool call and tool return is demonstrated as a necessary part of internal chat progress to retain, by the error just made - inability to see any success.


There could be a bit of time when your function’s code is working, such as calling an external API or computing. You can send something to the user interface that there is a tool call emitted and processing.

You are “streaming” both cases - content delta events to a UI, function events to code. When all functions requested are satisfied (potentially async), then you add the collected events and the returns to chat history (or return as input to a previous response id), and call the API again.

Well, thank you so much!

Got the tool_calls to work in a single Agent, so thank you (btw, not using SDK as my implementation is not through Python…)

SO, now the big question is - how do you handle Handoffs??

I set them up as tools, and created a triage agent, sent that up, and the response back picks the right Agent, and then I parse that and send it back up to the backend to run that Agent, almost as if a tool BUT

the response just dies - which was the same thing it was doing until I properly formatted the input object to be a function_call_output type, with the answer in there.

so then how do we go about replying to the function_call of a transfer to agent in the conversation history? First attempts were just giving the response to that transfer request - which were the arguments for the transfer, etc. but again, just silence…

EDIT: nvm!! It WORKED! oh man…this is crazy, opens up so many possibilities…

still would like to know if I’m handling this correctly - I mean, it transferred to the right agent, ran the file_search on that agent (a tool it has) and streamed back a great response…but if you have any thing to add, please let me know - thanks!

About the only thing is that your automatic iterator that handles responses from the AI must continue to handle continued tool calls and returning them to be run again, until the AI is tired of using tools and only gives you user-facing content.

And that both some text to display and a tool-call can be output in one response.

1 Like

Mmm I’m still struggling with this. My code sometimes works, and sometimes doesn’t - clearly I’m doing something incorrect.

try {
      const previousResponseId = // grab from DB if exists
      const input: OpenAI.Responses.ResponseInputItem[] = [
        {
          role: "user",
          content: message,
        },
      ];

      // response 1
      response = await createResponse({
        model,
        previous_response_id: previousResponseId,
        instructions,
        input,
        tools,
        user,
      });

      const toolCalls = response.output.filter(
        (o) => o.type === "function_call"
      );

      if (toolCalls.length > 0) {
        const toolResults = await handleToolCalls(toolCalls);

         // Non-sequential appending attempt - sometimes works
         // input.push(...toolCalls)
         // input.push(...toolResults)

        // add tool calls to input sequentially; input -> call -> call_result -> call ...
        for (const call of toolCalls) {
          input.push(call); 
          input.push(toolResults.find((r) => r.call_id === call.call_id)!); 
        }

        // response 2
        response = await createResponse({
          model,
          previous_response_id: response.id,
          instructions,
          input,
          tools,
          user,
        });
      }

      return {
        response: response.output_text,
        response_id: response.id, // to be saved to DB as previousResponseId
      };
    } catch (error) {
      throw error
    }

Doing it this way will give me the Duplicate item found with id fc_68596faec4fc819dac926d7... error.

I’m not entirely sure I’m using previous_response_id correctly; I’ve tried following examples in the docs (as well as on this thread). No luck so far. What am I doing wrong?

UPDATE:

In the above code, I’m passing the first response’s ID to the second (as previous_response_id). However, if I don’t do that, and just pass in the initial previous_response_id (from the DB), things seem to work…sometimes. Every now and then I’ll get the No tool call found error.

I don’t know why it succeeds some times and fails others (with tools calls).