A black basalt wall with one thin vertical slot and a tall arched opening; a broad violet beam from the left narrows into a thread of light along the slot, while a ribbon of light bends through the arch
All posts

'Function tools with reasoning_effort are not supported': the fix depends on the model

Chat Completions rejects function tools combined with reasoning_effort on GPT-5.4 and later. Which models accept none, which need Responses, and a gateway workaround.

On 3 October a Frigate user reported that chat failed on every message, with Function tools with reasoning_effort are not supported for gpt-6-sol in /v1/chat/completions in the log. A collaborator answered with the fix the message suggests, reasoning_effort: none under runtime_options. The reporter confirmed it on gpt-6-sol, but on gpt-6.1-sol the same setting returned a different 400, Unsupported value: 'reasoning_effort' does not support 'none' with this model, and the reporter wrote that the model "will always fail". The error text gives the same advice for both models, "use /v1/responses or set reasoning_effort to 'none'", although GPT-6.1 Sol rejects none.

Chat Completions rejects function tools when the reasoning effort is anything but none, and GPT-6 Astra and GPT-6.1 Sol, which do not accept none at all, are left with Responses only. That matters for anyone migrating, because the replacements OpenAI lists for the models it retires on 11 December 2026 and 14 January 2027 all fall under the rule; the GPT-5 and o3 guide and the GPT-4o and GPT-4.1 guide cover it for their models.

A request that never sets reasoning_effort can still fail

A request with tools and no reasoning_effort fails on gpt-5.6-luna:

{
  "model": "gpt-5.6-luna",
  "messages": [{"role": "user", "content": "What is the weather in Lisbon?"}],
  "tools": [{"type": "function", "function": {"name": "get_weather", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}}}}]
}

OpenAI answers with a 400. The Frigate log prints this body for the same model as a Python dict, shown here as JSON:

{
  "error": {
    "message": "Function tools with reasoning_effort are not supported for gpt-5.6-luna 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": null
  }
}

The model fills in the missing effort, and on most of these models it is not none. OpenAI's reasoning guide gives medium as the default for gpt-5.5, GPT-5.6, GPT-6.1 Sol, GPT-6 Sol and GPT-6 Luna, and the GPT-6 guide gives it for GPT-6.1 Terra; GPT-6 Astra has no none to fall back on. gpt-5.4, whose model page lists none as its default, is the exception.

The GPT-5.6 upgrade guide spells out what follows: usages of gpt-5.4 and its mini and nano "commonly defaulted to none", so a previously omitted setting "can therefore become slower, more expensive, and incompatible with Chat Completions function tools after the model swap." "More expensive" is literal, because reasoning tokens "are billed as output tokens" and take space in the context window. They also count against the cap: Chat Completions defines max_completion_tokens as covering visible output and reasoning tokens, so code that sized the cap for the answer alone, as it could while the effort was none, can end a response with finish_reason: "length" while the reasoning is still billed. On Responses the same case is a response with status incomplete, and OpenAI suggests reserving at least 25,000 tokens for reasoning and output when you start.

A developer in the OpenAI community forum reported the same break on moving from gpt-5.4 to gpt-5.6-sol, and wrote that forcing every call to none "was not required with prior models".

Frigate users hit it without ever setting an effort. The collaborator who answered explained that Frigate sends no reasoning_effort and that the newer models reason by default, and a collaborator on OpenAI's Python Agents SDK said the same about GPT-6 Sol. The reporter noted that Frigate's description feature, which sends no tools, kept working on the same models. OpenAI's migration guide has a Structured Outputs example that sends reasoning_effort: "medium" to gpt-6-astra on Chat Completions with no tools in it.

The restriction is older than the GPT-6 family. A Pipecat report from March shows the message for gpt-5.4, with the shorter advice "Please use /v1/responses instead", and OpenAI's migration guide states the rule in one sentence: "Starting with GPT-5.4, Chat Completions does not support tool calling with reasoning_effort values other than none." An OpenAI support reply in the forum thread, dated 7 September, says Responses supports the tool workflow but that a fix for this exact Chat Completions combination is not confirmed, so I would plan on the rule staying. Chat Completions itself "remains supported" in the migration guide.

Setting reasoning_effort to none fails on GPT-6 Astra and GPT-6.1 Sol

OpenAI's pages document the Frigate fix for the first row and point the other three models to Responses.

What OpenAI's pages say about the none effort and function tools on Chat Completions, by model
ModelAccepts noneFunction tools on Chat CompletionsFix
gpt-5.4, gpt-5.5, gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-6-sol, gpt-6-lunaYesOnly with nonenone, or Responses
gpt-6.1-terraYesNot statedResponses
gpt-6-astraNo: HTTP 400Not supportedResponses
gpt-6.1-solNoNot supportedResponses

The gpt-6-sol and gpt-6-luna pages put the first row's rule for the GPT-6 models in one line: "Chat Completions supports function calling only with reasoning_effort set to none."

GPT-6 Astra fails the way GPT-6.1 Sol did for the Frigate reporter: the reasoning guide says none returns HTTP 400 for it and that Chat Completions does not support function calling with either model. The error text still advises none for both models, and the Cherry Studio thread quotes that advice for gpt-6-astra in four comments and for gpt-6.1-sol in six.

GPT-6.1 Terra's page lists none among the supported efforts and says "Tool calling requires the Responses API", and the GPT-6 guide groups it with Astra and GPT-6.1 Sol on tools while naming it with Sol and Luna on none. Neither page says whether none lets Terra call tools on Chat Completions, so I list Responses as the fix.

The Frigate fix works where none is accepted, but OpenAI describes the setting as for "Latency-critical tasks that do not benefit from any reasoning or multi-chained tool calls", and an agent that chains tool calls is the opposite case. I would call none a stopgap for a client I cannot edit, and run a sample of real tasks before leaving it in place.

Responses keeps the tool loop and changes its item shapes

When the code is yours, I would move the tool loop to Responses. The loop below runs a function call on gpt-6.1-sol at medium effort, the combination Chat Completions refuses, and sends back everything the model produced:

import json
from openai import OpenAI

client = OpenAI()

tools = [{
    "type": "function",
    "name": "get_weather",
    "description": "Get the current weather for a city.",
    "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"],
    },
}]


def get_weather(city: str) -> dict:
    return {"city": city, "temp_c": 21}


input_items = [{"role": "user", "content": "What is the weather in Lisbon?"}]

while True:
    response = client.responses.create(
        model="gpt-6.1-sol",
        reasoning={"effort": "medium"},
        input=input_items,
        tools=tools,
    )
    input_items += response.output

    calls = [item for item in response.output if item.type == "function_call"]
    if not calls:
        break

    for call in calls:
        result = get_weather(**json.loads(call.arguments))
        input_items.append({
            "type": "function_call_output",
            "call_id": call.call_id,
            "output": json.dumps(result),
        })

print(response.output_text)

The request and the loop change like this:

  • Function tools are flat: name, description and parameters sit next to "type": "function", with no function wrapper.
  • The effort moves to reasoning: {"effort": "medium"}.
  • A tool request is an output item of type function_call with a call_id, and the result goes back as a function_call_output item with the same call_id.
  • response_format becomes text.format, and max_completion_tokens becomes max_output_tokens.
  • A function that omits strict is normalized to strict mode on Responses when its schema allows, while Chat Completions stays non-strict, so check your schemas.

The input_items += response.output line also sends back the reasoning items. OpenAI recommends passing them back with the function call and its output so the model can continue the reasoning it started. With store: false or under Zero Data Retention, the guide says those items carry encrypted_content by default, with no include flag needed, and the reasoning post covers what reasoning state has to survive a tool call.

For the GPT-6 family, OpenAI's checklist says to remove temperature, top_p and top_logprobs whenever the effort is not none. The temperature 400 comes from that rule, and only some GPT-6 models can run at none to avoid it.

Most reports come from software the reporter did not write

Frigate, Pipecat, LibreChat, Cherry Studio and a JetBrains provider all report the 400 from software that builds the request itself. Where the client exposes the field, the smallest fix is the one the Frigate collaborator gave: set reasoning_effort: none under runtime_options in the chat provider's config, on a model that accepts none. A LibreChat report on gpt-5.6-terra was closed as a duplicate in just over an hour. The Pipecat report set reasoning_effort to low on gpt-5.4, a contributor replied that the project was adding a Responses module, and the issue closed the next day.

OpenAI's own Python Agents SDK hit it too. Issue 5336 reports that the SDK sent no reasoning setting for GPT-6 models, so function tools failed on Chat Completions. Pull request 5420, merged on 26 October, defaults the effort to none when a Chat Completions request carries tools and no effort, but only for the exact names gpt-6-sol and gpt-6-luna. gpt-6.1-sol and gpt-6-astra are left out, and the pull request's tests assert that no default is applied to them.

The busiest thread is Cherry Studio's, still open. It holds 53 comments since 22 October, all posted by the project's feedback bot, with 52 distinct diagnostic IDs, so the count is of occurrences and says nothing about the number of users. Of the 46 that quote OpenAI's English message, 39 name the model, and those names run from gpt-5.4 to gpt-6.1-sol. Some arrive through relays that reword the message, so a search for OpenAI's exact wording misses them; one says gpt-6-sol "requires Responses for tool calls with reasoning".

When the client offers no setting, one choice is a model that works without one: gpt-5.4, whose default is none, or an older model that predates the rule, though GPT-5 and o3 retire on 11 December 2026 and GPT-4o and GPT-4.1 on 14 January 2027, per OpenAI's deprecations page. The other choice is a hop between the client and OpenAI that speaks Responses upstream.

An AIVAX gateway can send Responses requests upstream for a Chat Completions client

AIVAX's public inference endpoint is /v1/chat/completions, and the AI Gateway guide says /v1/responses is not supported. That sentence is about the endpoint your client calls. What a gateway sends to the provider is a separate setting: a gateway that uses your own provider key (BYOK) takes a transportInterface parameter, which the API reference lists with the values OpenAiCompatible, OpenAiChatCompletions and OpenAiResponses. The first two are the same transport, Chat Completions, which is the default.

With OpenAiResponses, the gateway becomes a bridge: it rewrites each Chat Completions request as a Responses request and posts it to the provider's /responses path. This is the creation call:

curl https://inference.aivax.net/api/v1/ai-gateways \
  -H "Authorization: Bearer $AIVAX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "OpenAI via Responses",
    "parameters": {
      "baseAddress": "https://api.openai.com/v1/responses",
      "apiKey": "<YOUR_OPENAI_API_KEY>",
      "modelName": "gpt-6.1-sol",
      "transportInterface": "OpenAiResponses",
      "reasoningEffort": "medium"
    }
  }'

The response carries the new gateway's ID in data.aiGatewayId. A client passes that ID, or the slug that listing the gateways returns, as model on the usual endpoint, as in the guide's SDK example, and keeps sending tools in the Chat Completions shape. The gateway's reasoningEffort applies when the client leaves the field out, which is the Frigate case, and a reasoning_effort in the request overrides it. Set to none on a gateway that keeps the default transport, the same field is a server-side version of the Frigate fix for a client that never sends an effort, on a model that accepts none. The gateway stores your OpenAI key, and the guide notes that reading a gateway back returns it in plain text to the owning account.

Inside the gateway, OpenAIResponsesTranslator turns reasoning_effort into reasoning: {"effort": "medium", "summary": "auto"}, strips the function wrapper from each tool, renames max_completion_tokens to max_output_tokens, and adds stream: true and store: false. A short allow-list decides what else survives, and everything outside it is removed without an error: stop, seed, service_tier, prompt_cache_key and safety_identifier among them. A client that depends on stop sequences or a seed loses them on this transport, and the cache hit rate post covers what losing prompt_cache_key means. temperature and top_p also pass through, and the checklist above says to remove both when the effort is not none.

A tool that is not a function, or a json_object response format, makes the translator throw, and AIVAX reports that as a server_error with a generic message instead of a 400 that names the cause.

On the way back, every response.output_text.delta becomes content, and nothing reads a message's phase. A commentary message followed by a final answer reaches a streaming client as one string, "I'll look that up. It is 21 C.", and replayed assistant messages go out without a phase, so a long tool flow that depends on it loses it at the bridge. OpenAI documents phase for gpt-5.5 and gpt-5.4 in tool-heavy flows and says that dropping it can make preambles read as final answers; those are the two models where I would look. A response that ends on max_output_tokens reaches a streaming client as a chunk with finish_reason: "error" and the message max_output_tokens: the gateway reports the Responses incomplete status as an error, where a direct Chat Completions call would end with length.

The limit that matters most applies to tool loops that run on the client. The public endpoint returns the reasoning text, never reasoning_details. Because the translator always sets store: false, encrypted reasoning items are the only way to carry the reasoning forward, and a client cannot send back items it never received; the gateway carries them only through its own server-side tool loop. A client-managed loop through the bridge gets past the 400 without the reasoning continuity that Responses offers. I would use the bridge for software I cannot change and move code I can change to Responses directly.

Integrated models, which a client selects with a tag such as @openai/gpt-6-luna or through a gateway whose base address is @integrated, do not use the bridge: the transport comes from the model's provider, and the providers in AIVAX's catalog send Chat Completions. Nothing in AIVAX's code adapts tools plus an effort for them, and a provider's 400 is passed through to the client, so the outcome depends on the provider behind the tag.

A fix that works on one model has to be replayed on the next

An omitted effort is none on gpt-5.4, where tool calls worked without anyone choosing it, and medium on the newer models that default to it, so the same request passes on one and fails on the next. Keep the request that failed and replay it whenever the model ID changes or an alias moves; after a fix the 400 should be gone and the response should contain a tool call (tool_calls on Chat Completions, a function_call item on Responses). If none was your fix, compare a sample of real tasks with the answers you got before the change. The post on pinning model IDs versus aliases covers how to notice a moved alias.

Read on 29 October 2026: OpenAI's documentation, the linked threads and pull request, and AIVAX's guides, API reference and gateway code. On November 10, 2026 I sent the gateway creation request above to the live AIVAX API with a placeholder provider key: it returned data.aiGatewayId, reading the gateway back showed transportInterface OpenAiResponses, reasoningEffort medium and the provider key in plain text, and I deleted it afterwards. POST /v1/responses on the public AIVAX endpoint returned 404. The error texts come from public reports, the bridge is described from its source and an offline run on synthetic payloads and events, and the Python loop ran with openai 2.41.0 over a mocked HTTP transport. What an integrated model such as @openai/gpt-5.6-sol returns for tools plus an effort, and what OpenAI does with temperature or top_p sent alongside one, come from OpenAI's documentation and the gateway source. The Cherry Studio counts are those of the thread that day, and I found no shutdown date for Chat Completions on OpenAI's deprecations page.