How to handle tool outputs in Responses API
tools, check if response.choices[0].finish_reason equals tool_calls. Then parse the tool call arguments from response.choices[0].message.tool_calls[0].function.arguments as JSON to handle tool outputs correctly. This ensures your app can respond to tool invocations properly.code_error finish_reason for tool_calls and parse tool_calls[0].function.arguments JSON to handle tool outputs.Why this happens
When you call the OpenAI Responses API with tools enabled, the model may respond with a finish_reason of tool_calls instead of stop or length. This indicates the model wants to invoke a tool function. However, if your code only reads response.choices[0].message.content without checking for tool calls, you miss the tool output data. This leads to incomplete or incorrect handling of the response.
Typical broken code example:
from openai import OpenAI
import os
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
response = client.chat.completions.create(
model="gpt-4o-mini",
tools=[{"type": "function", "function": {
"name": "get_weather",
"description": "Get weather for a location",
"parameters": {
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"]
}
}}],
messages=[{"role": "user", "content": "What's the weather in NYC?"}]
)
# Incorrect handling
print(response.choices[0].message.content) # May be empty or incomplete '' # Empty or missing tool output
The fix
Check if finish_reason is tool_calls. If so, extract the tool call arguments from response.choices[0].message.tool_calls[0].function.arguments and parse it as JSON. This gives you the structured tool output to process or forward to your tool handler.
This works because the Responses API separates tool invocation metadata from the normal message content, requiring explicit parsing.
from openai import OpenAI
import os
import json
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
response = client.chat.completions.create(
model="gpt-4o-mini",
tools=[{"type": "function", "function": {
"name": "get_weather",
"description": "Get weather for a location",
"parameters": {
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"]
}
}}],
messages=[{"role": "user", "content": "What's the weather in NYC?"}]
)
choice = response.choices[0]
if choice.finish_reason == "tool_calls":
tool_call = choice.message.tool_calls[0]
args = json.loads(tool_call.function.arguments)
print(f"Tool called: {tool_call.function.name}")
print(f"Arguments: {args}")
else:
print(choice.message.content) Tool called: get_weather
Arguments: {'location': 'NYC'} Preventing it in production
- Implement retry logic with exponential backoff to handle transient API errors.
- Validate
finish_reasonon every response to detect tool calls reliably. - Use schema validation on parsed tool arguments to avoid malformed inputs.
- Design your app to handle both normal content and tool call outputs gracefully.
Key Takeaways
- Always check
finish_reasonfortool_callsto detect tool outputs. - Parse
tool_calls[0].function.argumentsas JSON to extract tool parameters. - Use the
tools=parameter in the API call to enable tool usage. - Implement robust error handling and validation for tool output parsing.
- Keep your SDK and API usage up to date to avoid missing fields or deprecated parameters.