BadRequestError
openai.BadRequestError (HTTP 400)
Stack trace
openai.BadRequestError: Error code: 400 - {'error': {'message': "Invalid value for 'reasoning_effort': must be one of 'low', 'medium', 'high'", 'type': 'invalid_request_error', 'param': 'reasoning_effort'}} Why it happens
The o3-mini reasoning model requires the reasoning_effort parameter to control inference cost and latency tradeoffs. Only three discrete values ('low', 'medium', 'high') are valid; passing strings like 'minimal', 'balanced', 'maximum', or numeric values triggers a 400 Bad Request error from OpenAI's validation layer.
Detection
Log all reasoning_effort values in your model calls and validate against the set {'low', 'medium', 'high'} before sending to OpenAI. Use type hints (Literal['low', 'medium', 'high']) to catch invalid values at development time.
Causes & fixes
Using an unsupported string value like 'minimal', 'balanced', 'maximum', or 'extreme' instead of the three valid values
Replace with one of the three supported values: 'low' (fastest, cheapest), 'medium' (balanced), or 'high' (most thorough reasoning). Example: reasoning_effort='medium'
Passing a numeric value (1, 2, 3) or None instead of a string
Ensure reasoning_effort is always a string: 'low', 'medium', or 'high'. If using a variable, wrap it with str() or validate its type before the API call.
Using an environment variable or config that contains whitespace or case variation (e.g., 'Low', ' medium ', 'LOW')
Normalize the value: use .lower().strip() on the input before passing to OpenAI. Example: reasoning_effort = os.getenv('REASONING_EFFORT', 'medium').lower().strip()
Omitting reasoning_effort or passing it for models that don't support it (gpt-4o, gpt-4o-mini)
Only set reasoning_effort for o-series reasoning models (o3, o3-mini, o1). Remove it from non-reasoning model calls or conditionally include it only when model name contains 'o3' or 'o1'.
Code: broken vs fixed
import os
from openai import OpenAI
client = OpenAI(api_key=os.environ.get('OPENAI_API_KEY'))
# BROKEN: invalid reasoning_effort value
response = client.chat.completions.create(
model='o3-mini',
messages=[{'role': 'user', 'content': 'Solve: 2+2'}],
reasoning_effort='maximum' # ❌ INVALID — only 'low', 'medium', 'high' allowed
)
print(response.choices[0].message.content) import os
from openai import OpenAI
client = OpenAI(api_key=os.environ.get('OPENAI_API_KEY'))
# FIXED: use valid reasoning_effort value and normalize from env
reasoning_effort = os.environ.get('REASONING_EFFORT', 'medium').lower().strip()
if reasoning_effort not in ['low', 'medium', 'high']:
raise ValueError(f"reasoning_effort must be 'low', 'medium', or 'high', got {reasoning_effort}")
response = client.chat.completions.create(
model='o3-mini',
messages=[{'role': 'user', 'content': 'Solve: 2+2'}],
reasoning_effort=reasoning_effort # ✅ Valid value: 'low', 'medium', or 'high'
)
print(response.choices[0].message.content) Workaround
If you must support multiple reasoning effort levels in a config system that uses different naming, create a mapping dictionary: effort_map = {'minimal': 'low', 'balanced': 'medium', 'maximum': 'high', 'extreme': 'high'} and translate the config value before passing to OpenAI.
Prevention
Use Python Literal type hints in your function signatures: from typing import Literal; def call_o3(effort: Literal['low', 'medium', 'high']) -> ... to catch invalid values at development time with mypy. For config-driven systems, validate against a whitelist before any API call.