High severity HTTP 400 intermediate · Fix: 5-10 min

BadRequestError

openai.BadRequestError (HTTP 400)

What this error means
OpenAI o1 and o1-mini reasoning models do not support function calling, tool_choice parameters, or the tools parameter: these features are disabled by design to preserve reasoning chain integrity.

Stack trace

traceback
openai.BadRequestError: Error code: 400 - {'error': {'message': "This model doesn't support function calling", 'type': 'invalid_request_error', 'param': 'tools', 'code': 'unsupported_feature'}}
QUICK FIX
Remove tools=[], tool_choice, and system messages from o1 requests. Use client.chat.completions.create(model='o1', messages=[{'role': 'user', 'content': prompt}]) with JSON schema instructions in the prompt instead.

Why it happens

OpenAI's o1 and o1-mini reasoning models use a proprietary chain-of-thought process that is incompatible with function calling and tool use. The model's reasoning chain must remain opaque and uninterrupted. OpenAI deliberately removed support for tools, tool_choice, and parallel function calling to prevent developers from breaking the reasoning pipeline. If you pass the tools or tool_choice parameter to o1, the API rejects the request with a 400 error before the model ever runs.

Detection

Check your code for any tools=[], tool_choice parameter, or function definitions in messages before sending to o1/o1-mini. Use IDE search: grep -r 'tool_choice' and grep -r 'tools=' on your request builder. Add logging to print the full request before sending: print(json.dumps(client.chat.completions.create.__dict__)). Consider using model detection: if model in ['o1', 'o1-mini']: remove_tools_from_request().

Causes & fixes

1

Attempting to use tools or tool_choice parameter with o1/o1-mini models

✓ Fix

Remove the tools=[] and tool_choice parameters entirely. o1 does not support function calling. Switch to gpt-4o or gpt-4o-mini if you need tool use, or use o1 with JSON schema validation in the prompt instead.

2

Using a wrapper library (LangChain, LiteLLM) that automatically adds tools to all requests

✓ Fix

Configure your wrapper to explicitly skip tool injection for o1 models. In LangChain: model_name='o1' and add a check to disable bind_tools(). In LiteLLM: add o1 to the NO_TOOLS_MODELS list or use completion mode.

3

Reusing request builder code from gpt-4o that includes tools parameter

✓ Fix

Create a separate code path for o1: def call_reasoning_model(prompt): client.chat.completions.create(model='o1', messages=[{'role': 'user', 'content': prompt}]): no tools, no tool_choice, no system message.

4

System messages are also not supported in o1/o1-mini for reasoning chain clarity

✓ Fix

Move all system instructions into the user message. Instead of system='You are an expert...', use: messages=[{'role': 'user', 'content': 'You are an expert. ' + user_prompt}].

Code: broken vs fixed

Broken - triggers the error
python
import os
import json
from openai import OpenAI

client = OpenAI(api_key=os.environ.get('OPENAI_API_KEY'))

# BROKEN: Trying to use function calling with o1 — will fail with 400 error
tools = [
    {
        'type': 'function',
        'function': {
            'name': 'calculate_sum',
            'description': 'Add two numbers',
            'parameters': {
                'type': 'object',
                'properties': {
                    'a': {'type': 'number'},
                    'b': {'type': 'number'}
                }
            }
        }
    }
]

response = client.chat.completions.create(
    model='o1',  # This line will trigger 400 error because o1 doesn't support tools
    messages=[{'role': 'user', 'content': 'What is 5 + 3?'}],
    tools=tools,  # BROKEN: o1 rejects this
    tool_choice='auto'  # BROKEN: o1 rejects this
)

print(response.choices[0].message.content)
Fixed - works correctly
python
import os
import json
from openai import OpenAI

client = OpenAI(api_key=os.environ.get('OPENAI_API_KEY'))

# FIXED: Use o1 without function calling — include JSON schema in the prompt instead
user_message = """Answer the following and respond with ONLY valid JSON, no markdown fences.
{
  "sum": <number>,
  "explanation": <string>
}

Question: What is 5 + 3?"""

response = client.chat.completions.create(
    model='o1',  # FIXED: o1 works without tools/tool_choice
    messages=[{'role': 'user', 'content': user_message}]
    # REMOVED: tools=[], tool_choice='auto' — o1 doesn't support these
)

response_text = response.choices[0].message.content
print('Raw response:', response_text)

# Parse JSON from response
try:
    result = json.loads(response_text)
    print('Sum:', result['sum'])
    print('Explanation:', result['explanation'])
except json.JSONDecodeError:
    print('Failed to parse JSON from o1 response')
Removed tools and tool_choice parameters entirely — o1 doesn't support them. Instead, embedded JSON schema directly in the prompt and parse the response with json.loads(). o1's reasoning chain stays intact without tool interruption.

Workaround

If you must keep the same request code for both gpt-4o and o1, wrap the tools parameter in a conditional: if model == 'o1': request_params = {'model': model, 'messages': messages} else: request_params = {'model': model, 'messages': messages, 'tools': tools, 'tool_choice': 'auto'}. Then call: response = client.chat.completions.create(**request_params). This defers tool use for o1 until a future API update (unlikely) while keeping gpt-4o working unchanged.

Prevention

Design your LLM request logic with model-specific code paths from the start. Create separate functions: def call_reasoning_model(prompt) for o1/o1-mini without tools, and def call_tool_model(prompt, tools) for gpt-4o. Add a model registry that maps model names to their capabilities: MODELS_WITH_TOOLS = {'gpt-4o', 'gpt-4o-mini'}, MODELS_WITHOUT_TOOLS = {'o1', 'o1-mini'}. Check this registry before building requests. Use TypedDicts or Pydantic to validate request structure and reject invalid parameter combinations at runtime before sending to API.

Python 3.9+ · openai >=1.3.0 · tested on 1.30.x
Verified 2026-04 · o1, o1-mini, gpt-4o, gpt-4o-mini
Verify ↗

Community Notes

No notes yetBe the first to share a version-specific fix or tip.