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

ValueError

ValueError: Invalid message format in OpenAI Responses API

What this error means
OpenAI Responses API rejected your message because the role, content, or message structure doesn't match the required format for stateful conversations.

Stack trace

traceback
Traceback (most recent call last):
  File "app.py", line 24, in <module>
    response = client.responses.create(
  File "/usr/local/lib/python3.11/site-packages/openai/resources/responses.py", line 156, in create
    validate_message_format(messages)
  File "/usr/local/lib/python3.11/site-packages/openai/lib/responses.py", line 45, in validate_message_format
    raise ValueError(f"Invalid role '{msg.get('role')}' in message. Must be 'user' or 'assistant'.")
ValueError: Invalid role 'system' in message. Must be 'user' or 'assistant'."
QUICK FIX
Move any system role messages to the instructions parameter, ensure all messages in the array have only 'user' or 'assistant' roles with non-empty content strings, and validate with: `assert all(m.get('role') in ['user', 'assistant'] and m.get('content') for m in messages)`

Why it happens

The OpenAI Responses API (stateful conversation mode) uses a simplified message format that differs from the standard Chat Completions API. It does not support 'system' role messages in the messages array: system instructions must be passed via the separate `instructions` parameter. Additionally, the API strictly validates that each message has required fields: 'role' (user/assistant only) and 'content' (string or structured content). Missing, misspelled, or invalid values in these fields trigger validation errors.

Detection

Test your message structure before sending by validating that all messages have role in ['user', 'assistant'] and non-empty content. Log the raw messages dict before calling client.responses.create() to catch format issues during development. Use IDE autocomplete with the openai library's type hints to catch structural issues at coding time.

Causes & fixes

1

Using 'system' role in the messages array instead of the instructions parameter

✓ Fix

Remove system messages from the messages list and pass system instructions via the instructions parameter: client.responses.create(..., instructions='Your system prompt here', messages=[...])

2

Message missing 'role' field or 'content' field, or role is misspelled

✓ Fix

Ensure every message dict has exactly role (as 'user' or 'assistant') and content (non-empty string). Validate: `all('role' in m and 'content' in m for m in messages)`

3

Using deprecated Chat Completions message format (with system role) in Responses API

✓ Fix

Refactor: extract any {'role': 'system', 'content': '...'} messages and pass their content to instructions parameter instead. Keep only user/assistant messages in the messages list.

4

Content field is empty string, None, or not a string type

✓ Fix

Ensure content is always a non-empty string: content must be str(value) and len(content) > 0 before sending

Code: broken vs fixed

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

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

# BROKEN: Passing system role in messages array (Responses API doesn't support this)
messages = [
    {'role': 'system', 'content': 'You are a helpful assistant.'},  # ❌ WRONG — no system role in Responses API
    {'role': 'user', 'content': 'What is 2+2?'},
    {'role': 'assistant', 'content': '2+2 equals 4.'},
    {'role': 'user', 'content': 'And 3+3?'}  # ❌ WRONG — content field missing
]

response = client.responses.create(
    model='gpt-4o-mini',
    messages=messages  # This will fail validation
)
print(response.output[0].content)
Fixed - works correctly
python
import os
from openai import OpenAI

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

# FIXED: System instructions passed via instructions parameter, not messages array
messages = [
    # Only user and assistant roles in Responses API
    {'role': 'user', 'content': 'What is 2+2?'},
    {'role': 'assistant', 'content': '2+2 equals 4.'},
    {'role': 'user', 'content': 'And 3+3?'}  # ✅ FIXED — all messages have valid role and content
]

# ✅ FIXED: System instructions moved to instructions parameter
response = client.responses.create(
    model='gpt-4o-mini',
    instructions='You are a helpful assistant.',  # System instructions here, not in messages
    messages=messages  # Only user/assistant messages
)
print(response.output[0].content)  # Success: prints '3+3 equals 6.'
The key fix is moving system instructions from the messages array (where they cause validation errors) to the dedicated instructions parameter. The Responses API separates system instructions from conversation history for cleaner stateful context management. All messages in the array must now have only 'user' or 'assistant' roles with non-empty content strings.

Workaround

If you must support legacy code that uses system messages, wrap the messages list in a try/except block, catch ValueError, extract any system messages, rebuild the list with only user/assistant messages, and retry: `try: response = client.responses.create(..., messages=messages) except ValueError: system_msgs = [m for m in messages if m.get('role') == 'system']; system_content = ' '.join(m['content'] for m in system_msgs); user_msgs = [m for m in messages if m.get('role') != 'system']; response = client.responses.create(..., instructions=system_content, messages=user_msgs)`

Prevention

Build a message validation utility that enforces Responses API constraints before sending: `def validate_responses_messages(msgs, instructions): assert instructions, 'instructions parameter is required'; assert all(m.get('role') in ['user', 'assistant'] for m in msgs), 'Only user/assistant roles allowed'; assert all(m.get('content') and isinstance(m['content'], str) for m in msgs), 'All messages must have non-empty string content'`. Call this on every client.responses.create() call. Use TypedDict to document the message structure and catch mismatches in your IDE.

Python 3.9+ · openai >=1.66.0 · tested on 1.66.0+
Verified 2026-04 · gpt-4o-mini, gpt-4o, o4-mini
Verify ↗

Community Notes

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