BUG REPORT: OpenAi 5.6 family fails ALL tool-calling via native OpenAi provider

Bug: OpenAI GPT-5.6 family fails ALL tool-calling via native OpenAI provider

crewai version: 1.15.1 (confirmed still present as of checking 1.15.2 and 1.15.3 changelogs — neither mentions this)

Summary

Any Agent using an OpenAI GPT-5.6-family model (gpt-5.6-sol, gpt-5.6-terra, or gpt-5.6-luna) fails immediately on the first LLM call if the agent has any tools attached. This makes the entire GPT-5.6 family unusable for agentic workflows in crewAI today.

Error

Error code: 400 - {'error': {'message': "Function tools with reasoning_effort are not supported for gpt-5.6-sol in /v1/chat/completions. To use function tools, use /v1/responses or set reasoning_effort to 'none'.", 'type': 'invalid_request_error', 'param': 'reasoning_effort', 'code': None}}

Same error (with the model name substituted) for gpt-5.6-terra and gpt-5.6-luna — confirmed it’s a whole-family issue, not model-specific.

Reproduction

from crewai.llm import LLM
from crewai import Agent, Task, Crew, Process

llm = LLM(model="openai/gpt-5.6-sol", max_completion_tokens=2048, api_key="...")
agent = Agent(role="Tester", goal="test", backstory="test", llm=llm, tools=[<any BaseTool>])
task = Task(description="Just say hello, do not call any tools.", expected_output="A greeting.", agent=agent)
Crew(agents=[agent], tasks=[task], process=Process.sequential).kickoff()
# -> BadRequestError as above

Tried, none resolve it:

  • Explicitly passing reasoning_effort="none" to LLM(...) — same error persists.
  • Routing through LiteLLM instead of the native provider (is_litellm=True) — same error, litellm doesn’t route this differently either.

What’s needed

OpenAI’s newer reasoning-tier models (this one specifically) require the Responses API (/v1/responses) for tool/function calling — the legacy Chat Completions endpoint (/v1/chat/completions) rejects tool calls outright for these models regardless of reasoning_effort value. crewai’s native OpenAI provider (crewai/llms/providers/openai/completion.py) appears to always use /v1/chat/completions for tool-calling agents; it would need to route reasoning-tier models needing tools through /v1/responses instead (the class already has some Responses-API-shaped fields — auto_chain_reasoning, reasoning.encrypted_content, max_output_tokens — so there may be partial groundwork already present that just isn’t wired into the tool-calling path).

Separate, smaller issues hit along the way (already worked around app-side, mentioning for completeness)

  1. GPT-5.6 (and GPT-5.5) reject the legacy max_tokens param outright (Unsupported parameter: 'max_tokens'... Use 'max_completion_tokens' instead) when using the native OpenAI provider. LiteLLM auto-translates this; the native provider does not. Not a bug report item since max_completion_tokens is a documented field already — just noting it’s easy to hit by surprise.
  2. A Pydantic BaseModel field literally named title collided with the auto-generated JSON-Schema "title" metadata key during OpenAI-strict-mode schema sanitization (pydantic_schema_utils.py), producing "'required' is required to be an array including every key in properties. Extra required key 'title' supplied." Worked around by renaming the field; flagging in case it affects other users with a title field on a tool’s args schema.

UPDATE / correction — root cause found, and my original diagnosis was partly wrong

Following up on my own report. After building a working around-fix I can now correct and sharpen this — the headline bug is real, but my proposed cause (“crewai would need to route reasoning models through /v1/responses”) was inaccurate: crewai already has a complete Responses API implementation. The real problem is three separate, more specific bugs. Two of them are in that Responses-API path itself and are directly actionable.

Correction: the Responses API path already exists

crewai/llms/providers/openai/completion.py already exposes a first-class field:

api: Literal["completions", "responses"] = "completions"

Passing LLM(model="openai/gpt-5.6-sol", api="responses", ...) bypasses the original 400 — the endpoint issue is solved by a flag that already ships. So the fix is not “add Responses routing.” It’s two things: (a) auto-select api="responses" for models that require it, and (b) fix the tool-calling loop bugs on that path (below). Today, even after setting api="responses" manually, a real tool-calling agent loop still fails.

The three actual bugs (found while making api="responses" work end-to-end)

Bug 1 — tool-call shape not recognized (silent failure). is_tool_call_list() and extract_tool_call_info() in crewai/utilities/agent_utils.py (used by the default crewai.experimental.agent_executor.AgentExecutor) recognize tool-call shapes for Chat Completions ({"function": {...}}), Anthropic/Bedrock ({"name", "input"}), and Gemini (.function_call) — but not the OpenAI Responses API’s own shape, which is {"id", "name", "arguments"}. Result: a genuine tool call is not detected as one, so it gets stringified and returned as the agent’s final answer. The tool never executes, no error is raised — silent wrong behavior.

Fix is additive — recognize the {"id", "name", "arguments"} shape (no "function", no "input") in both functions.

Bug 2 — message history not converted for the Responses API (400 on the follow-up call). OpenAICompletion._prepare_responses_params() builds the Responses API input array but passes non-system messages through unchanged. Once a tool executes, the history contains Chat-Completions-shaped messages:

  • assistant: {"role": "assistant", "content": null, "tool_calls": [...]}
  • tool result: {"role": "tool", "tool_call_id": ..., "content": ...}

Neither is valid Responses API input, so the next call fails:

400 - Invalid type for 'input[1].content': expected one of an array of objects or string, but got null instead.

The Responses API wants these as {"type": "function_call", "call_id", "name", "arguments"} and {"type": "function_call_output", "call_id", "output"} items respectively. _prepare_responses_params needs to translate assistant-with-tool_calls and tool-result messages into those item types.

With both of the above fixed, a full multi-turn tool-calling loop (tool call → execution → result fed back → final answer) works correctly on GPT-5.6 Sol/Terra/Luna.

Bug 3 — the title schema collision (already in my original report, confirmed). Still stands as reported: a tool args-schema field literally named title collides with JSON-Schema’s reserved "title" metadata key in the strict-mode sanitizer (pydantic_schema_utils.py), yielding "'required' is required ... Extra required key 'title' supplied."

Retraction

Please disregard the “smaller issue #1 from my original report (the max_tokens vs max_completion_tokens point). On reflection that’s documented behavior, not a bug — max_completion_tokens is the correct field and the native provider expecting it is reasonable. Not worth your time.

Net

  • The endpoint problem is solvable today via api="responses" (already shipped) — the missing piece is auto-selecting it for models that require it.
  • Bugs 1 and 2 are the substantive, actionable ones — both are provider-shape mismatches in the Responses-API tool-calling path, and both are needed for that path to work at all with tools.
  • Happy to share the exact monkey-patch bridge I’m using in the meantime if that’s useful as a reference for a real fix.