openai/openai-python · error · Exception
Expected Chat Completions function tool shape to be created
Error message
Expected Chat Completions function tool shape to be created using `openai.pydantic_function_tool()`
What it means
Raised by the internal _make_tools helper (used by Responses.stream/parse) when a tool has the Chat Completions function-tool shape (a dict with a 'function' key) but that function object is not a PydanticFunctionTool produced by openai.pydantic_function_tool(). The SDK auto-converts such tools for the Responses API and requires the canonical constructor.
Source
Thrown at src/openai/resources/responses/responses.py:3978
def _make_tools(tools: Iterable[ParseableToolParam] | Omit) -> List[ToolParam] | Omit:
if not is_given(tools):
return omit
converted_tools: List[ToolParam] = []
for tool in tools:
if tool["type"] != "function":
converted_tools.append(tool)
continue
if "function" not in tool:
# standard Responses API case
converted_tools.append(tool)
continue
function = cast(Any, tool)["function"] # pyright: ignore[reportUnnecessaryCast]
if not isinstance(function, PydanticFunctionTool):
raise Exception(
"Expected Chat Completions function tool shape to be created using `openai.pydantic_function_tool()`"
)
assert "parameters" in function
new_tool = ResponsesPydanticFunctionTool(
{
"type": "function",
"name": function["name"],
"description": function.get("description"),
"parameters": function["parameters"],
"strict": function.get("strict") or False,
},
function.model,
)
converted_tools.append(new_tool.cast())
return converted_toolsView on GitHub (pinned to 9917c6e28e)
Solutions
- Build the tool with openai.pydantic_function_tool(MyPydanticModel)
- Or use the native Responses tool shape: {'type': 'function', 'name': 'x', 'description': ..., 'parameters': {...}}
- When migrating from Chat Completions, strip the outer 'function' wrapper — Responses tools are flat
Example fix
// before
client.responses.parse(model="gpt-4o", input="hi", tools=[{"type": "function", "function": {"name": "get_weather", "parameters": {...}}}])
// after
client.responses.parse(model="gpt-4o", input="hi", tools=[{"type": "function", "name": "get_weather", "parameters": {...}}]) Defensive patterns
Strategy: type-guard
Validate before calling
def to_responses_tool(tool: dict) -> dict:
if tool.get('type') == 'function' and 'function' in tool:
fn = tool['function']
return {'type': 'function', **{k: v for k, v in fn.items() if k != 'strict'}}
return tool
tools = [to_responses_tool(t) for t in tools] Type guard
def is_responses_tool_shape(tool) -> bool:
return not (isinstance(tool, dict) and tool.get('type') == 'function' and 'function' in tool) Try / catch
try:
client.responses.parse(model=m, input=i, tools=tools)
except Exception as e:
if 'pydantic_function_tool' in str(e):
tools = [to_responses_tool(t) for t in tools]
client.responses.parse(model=m, input=i, tools=tools)
else:
raise Prevention
- Build tools with openai.pydantic_function_tool(MyModel) for pydantic schemas
- Use the flat Responses tool shape: {'type':'function','name':...,'parameters':...}
- Keep separate tool definitions for chat.completions and responses APIs
When it happens
Trigger: Passing a hand-built tool like {'type': 'function', 'function': {'name': ..., 'parameters': {...}}} (Chat Completions style) to client.responses.stream(tools=[...]) or parse(), instead of tools=[{'type':'function','name':...,'parameters':...}] or openai.pydantic_function_tool(MyModel).
Common situations: Copy-pasting Chat Completions tool definitions into Responses API calls; migrating from chat.completions.create(tools=...) without reshaping the tool dicts; building tools dynamically from raw dicts.
Related errors
- Didn't receive a `response.completed` event.
- Expected a non-empty value for `response_id` but received {r
- Expected a non-empty value for `response_id` but received {r
- Expected a non-empty value for `completion_id` but received
- You tried to pass a `BaseModel` class to `chat.completions.c
AI-assisted analysis of openai/openai-python@9917c6e28e (2026-08-28).
Data as JSON: /api/errors/ce29c59810f1e3f7.
Report an issue: GitHub.