A black basalt wall with one thin vertical slot and a tall arched opening; a broad violet beam stops at the slot while a ribbon of light bends through the arch
All posts

How to fix 'Function tools with reasoning_effort are not supported' in Chat Completions

Fix the 400 on gpt-5.6-sol Chat Completions: set reasoning_effort to none or move function tools to Responses. Includes AIVAX BYOK setup.

The 400 error Function tools with reasoning_effort are not supported for gpt-5.6-sol in /v1/chat/completions means the request combined function tools with a reasoning effort other than none on an OpenAI model that allows that combination only on the Responses API. There are two fixes: send reasoning_effort: "none" if the model accepts it, or call /v1/responses. OpenAI's docs date the restriction to GPT-5.4, and say Chat Completions does not support function calling at all with GPT-6 Astra or GPT-6.1 Sol, so those two need Responses.

Below: which fix fits which model, how to move a tool loop to Responses, and how an AIVAX gateway can send Responses requests upstream for a client that only speaks Chat Completions. OpenAI claims come from its docs and community threads as read on 13 October 2026, AIVAX claims from the gateway code and maintained docs. We ran no live requests against OpenAI or AIVAX managed models, so the error text is copied from public reports.

What does the "Function tools with reasoning_effort are not supported" error mean?

OpenAI returns it with param: reasoning_effort when a Chat Completions request has both tools and a reasoning effort other than none. A request like this fails:

curl https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.6-sol",
    "reasoning_effort": "medium",
    "messages": [{"role": "user", "content": "What is the weather in Lisbon?"}],
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get the current weather for a city.",
        "parameters": {
          "type": "object",
          "properties": {"city": {"type": "string"}},
          "required": ["city"]
        }
      }
    }]
  }'

The error body, as reported in public threads:

{
  "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": null
  }
}

It is easy to hit without setting the parameter yourself. A Frigate maintainer explained a user's failure this way: the app never sent reasoning_effort, but the newer models reason by default, so the request still counted as reasoning plus tools. The older wording of the message (Please use /v1/responses instead) appears in reports from March 2026 for gpt-5.4, so this is not new, and it has carried over to the GPT-6 family.

OpenAI's migration guide states the rule: "Starting with GPT-5.4, Chat Completions does not support tool calling with reasoning_effort values other than none." In the reasoning guide it adds that reasoning models "work better with the Responses API". In a community thread opened in July, an OpenAI support reply from 7 September says Responses supports the tool workflow but that OpenAI has not confirmed a fix for this exact Chat Completions combination.

Which fix works for which model?

What OpenAI documents or users report for function tools on Chat Completions, by model
ModelFunction tools on Chat CompletionsWhat to do
GPT-5.4 to GPT-5.6 (for example gpt-5.6-sol)Only with reasoning_effort: "none", per OpenAI's migration guideSend none if the model lists it, or use Responses
gpt-6-solA Frigate user reports that none works; we did not find this model covered in the docs we readTry none and keep a regression test, or use Responses
GPT-6 AstraNot supported. none itself returns HTTP 400Use Responses
GPT-6.1 SolNot supported. It accepts neither none nor minimalUse Responses
Models before GPT-5.4Not affected by this documented restrictionCheck the model's own capabilities

The GPT-6.1 Sol row matches a Frigate report: that user got the tools error with an effort set, and an "unsupported value" error when trying none. There is no setting that makes the combination work on Chat Completions for that model.

How do you fix it with reasoning_effort set to none?

Use this when the model accepts none and you need the smallest change. Add the field, or change the value:

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

The trade-off is that the model no longer reasons before calling the tool or answering. For routing, extraction and simple lookups this is often fine. For multi-step tool use it may lower quality, so run your own evaluation instead of assuming. OpenAI's guidance is that reasoning models work better with tools through Responses. The reasoning effort guide covers how each provider names this control.

How do you move a tool-calling loop to the Responses API?

Call POST /v1/responses and change the shapes. The differences that matter for a tool loop:

  • Function tools are flat: name, description and parameters sit next to "type": "function", with no function wrapper.
  • Effort moves to reasoning: {"effort": "medium"}.
  • The reply is a list of output items. A tool request is an item of type function_call with a call_id, not a tool_calls array on a message.
  • Your tool result goes back as an item of type function_call_output with the same call_id.
  • OpenAI recommends passing back the reasoning items returned with the function call, together with the call and its output.
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"],
    },
}]

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

response = client.responses.create(
    model="gpt-5.6-sol",
    reasoning={"effort": "medium"},
    input=history,
    tools=tools,
)

history += response.output

for item in response.output:
    if item.type == "function_call":
        history.append({
            "type": "function_call_output",
            "call_id": item.call_id,
            "output": '{"temp_c": 21}',
        })

response = client.responses.create(
    model="gpt-5.6-sol",
    reasoning={"effort": "medium"},
    input=history,
    tools=tools,
)

print(response.output_text)

Other changes to expect while migrating: response_format becomes text.format, max_completion_tokens becomes max_output_tokens, and streaming emits typed events instead of chat chunks. OpenAI also notes that omitting strict on a function in Responses tries strict mode, so check that your schemas still behave. The Assistants migration guide discusses which conversation state your application should own when you adopt Responses.

What if you cannot change the client?

The reports come mostly from software where the request body is not yours to edit: the OpenAI thread mentions GitHub Copilot in Visual Studio with a custom model, and there are open issues in LibreChat, pipecat and a JetBrains OpenAI-compatible provider. You have three options, in order of effort:

  1. Set the effort to none in the client's own configuration, if it exposes the option and the model accepts it. Frigate users solved it this way with runtime_options.
  2. Pick a model that is not restricted, such as one from before GPT-5.4.
  3. Put an OpenAI-compatible hop between the client and OpenAI that speaks Responses upstream. The client keeps sending Chat Completions; the hop translates.

How does AIVAX handle this combination?

AIVAX's public inference endpoint is /v1/chat/completions. The AI Gateway docs say /v1/responses is not supported, and that statement is about the endpoint your client calls. What a gateway sends upstream is a separate setting.

A gateway that uses your own provider key (BYOK) has a transportInterface field. Its values are OpenAiChatCompletions, the default, and OpenAiResponses. With OpenAiResponses, the gateway translates each Chat Completions request into a Responses request and sends it to the provider's /responses path. A base address that ends in /responses is used as is, and one that ends in /chat/completions is rewritten. The gateway body looks like this, for the gateway API (POST /api/v1/ai-gateways):

{
    "name": "OpenAI via Responses",
    "parameters": {
        "baseAddress": "https://api.openai.com/v1/responses",
        "apiKey": "<YOUR_OPENAI_API_KEY>",
        "modelName": "gpt-5.6-sol",
        "transportInterface": "OpenAiResponses",
        "reasoningEffort": "medium"
    }
}

The client then sends an ordinary Chat Completions request with tools, using the gateway's ID or slug as the model:

curl https://inference.aivax.net/v1/chat/completions \
  -H "Authorization: Bearer $AIVAX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "my-gateway:50c3",
    "messages": [{"role": "user", "content": "What is the weather in Lisbon?"}],
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get the current weather for a city.",
        "parameters": {
          "type": "object",
          "properties": {"city": {"type": "string"}},
          "required": ["city"]
        }
      }
    }]
  }'

A request-level reasoning_effort overrides the gateway value. BYOK traffic still passes through AIVAX and uses the plan's BYOK rate limit, described in the limits page.

What the translator does, and what we did not verify

We ran the translator's source offline against sample payloads. This checked the transformation only; it did not send anything to OpenAI.

  • reasoning_effort becomes reasoning: {"effort": ..., "summary": "auto"}.
  • Function tools lose the function wrapper. Any other tool type raises an error, and response_format is accepted only as json_schema.
  • An assistant message with tool_calls becomes function_call items, and a tool message becomes a function_call_output item with the same call_id. Arguments and outputs stay JSON strings.
  • Requests always stream and always set store: false, so you cannot rely on stored state to continue a conversation; the client supplies the history each turn.
  • stop, seed, stream_options, min_p and logprobs are dropped without an error, and max_completion_tokens becomes max_output_tokens. If you rely on stop or seed, they will not reach the model on this transport.

We did not test the gateway against OpenAI with a live key, so we cannot say how gpt-5.6-sol, gpt-6-astra or gpt-6.1-sol respond through it. We also did not test whether the 400 reaches a client that uses an AIVAX managed model such as @openai/gpt-5.6-sol; managed providers receive Chat Completions requests, and we did not check what they do with the combination.

On the way back, every response.output_text.delta event becomes content, and the code never reads a message's phase. If a model sends an intermediate message before its final answer in the same response, a streaming client receives both texts concatenated, with no separator. We found this by reading the code and did not reproduce it with a live model, so check the streamed text of your own tool loop for repeated or glued sentences.

The translator's request has no include field, so it does not ask OpenAI for encrypted reasoning, and the Chat Completions response does not promise to return reasoning details your client could send back. Do not assume that the reasoning items OpenAI recommends passing back survive a tool loop that your client manages. The reasoning post explains this limit; test it before relying on it.

How do you check the fix before you roll it out?

Keep the request that failed as a regression test and run it against each fix.

  1. Replay the original request with the fix applied (reasoning_effort: "none", or the Responses endpoint with your effort) and confirm the 400 is gone.
  2. Confirm the model actually calls the tool: on Chat Completions the choice has tool_calls; on Responses the output contains a function_call item.
  3. Complete the loop: send the result back and check that the final answer uses it.
  4. If you chose none, compare answer quality on a sample of your real tasks, not on the weather example.
  5. Repeat the test whenever you change the model ID or alias. OpenAI's own docs show the rules differ between GPT-5.6, GPT-6 Astra and GPT-6.1 Sol, which is the same hazard described in pinning a model ID versus using an alias.

FAQ

Is Chat Completions being removed?

OpenAI's migration guide says Chat Completions "remains supported" and recommends Responses for all new projects. We found no shutdown date in the pages we read. What is changing is which combinations of features it accepts.

Does the error happen without tools?

The restriction in OpenAI's docs is about tool calling, and every report we read includes function tools. A third-party migration guide (APIYI) says that without tools any effort the model supports is accepted; we did not confirm that in OpenAI's own docs, so test your model.

Does reasoning_effort: "none" work on every GPT-5.x and GPT-6 model?

No. OpenAI says GPT-6 Astra returns HTTP 400 for none, and that GPT-6.1 Sol supports neither none nor minimal. Check the model's page for its accepted efforts.

Can I use minimal or low with tools instead?

Not on Chat Completions. OpenAI's wording is that from GPT-5.4 only none works with tools there, so minimal, low and higher values hit the same error. GPT-6 Astra and GPT-6.1 Sol are stricter: they do not support function calling on Chat Completions at all.

Can an AIVAX gateway keep my Chat Completions client and still reach /responses?

For a BYOK gateway, the transportInterface setting does that, with the limits listed above. We have verified the request translation offline, not the live behavior of any specific model.

References