InvalidRequestError
openai.BadRequestError (HTTP 400): invalid_request_error
Stack trace
BadRequestError: Error code: 400 - {'error': {'message': "Invalid request: multiple images in content array. Please ensure each image is in its own content item with type 'image_url' and URL field. Reference: https://platform.openai.com/docs/guides/vision", 'type': 'invalid_request_error', 'param': 'messages[0].content', 'code': 'invalid_request_error'}} Why it happens
OpenAI's vision API enforces strict content array formatting: each image must be its own dict object in the messages[].content array with type='image_url' and a url field. Many developers accidentally nest multiple images into a single content dict, or pass raw image data instead of URLs. The API validates this structure at request time and rejects malformed payloads with a 400 Bad Request error.
Detection
Before sending vision requests to OpenAI, validate that each image is a separate dict in the content array with type='image_url'. Log the raw request body and inspect messages[0].content structure: if it's a list with a single dict containing multiple image URLs, you've found the bug. Add schema validation via Pydantic before making API calls.
Causes & fixes
Multiple images nested in a single content dict instead of separate dicts
Restructure content to place each image as its own dict: content=[{'type': 'image_url', 'image_url': {'url': 'url1'}}, {'type': 'image_url', 'image_url': {'url': 'url2'}}, {'type': 'text', 'text': 'prompt'}]
Passing base64 image data instead of image_url objects
Convert base64 images to URLs via OpenAI's data URI format: 'data:image/jpeg;base64,{base64_string}' or host images on a public URL and use standard https:// URLs
Missing 'url' field in image_url dict: only passing 'image_url' key without nested url
Use correct nested structure: {'type': 'image_url', 'image_url': {'url': 'https://...'}}: image_url must be a dict with a url key inside
Image URLs that are inaccessible or return non-image content types
Verify all image URLs are publicly accessible, return HTTP 200, have correct Content-Type headers (image/jpeg, image/png, image/webp, image/gif), and are under 20 MB per image
Code: broken vs fixed
import os
from openai import OpenAI
client = OpenAI(api_key=os.environ.get('OPENAI_API_KEY'))
# BROKEN: Multiple images in a single content item
response = client.chat.completions.create(
model='gpt-4o',
messages=[
{
'role': 'user',
'content': [
{
'type': 'image_url',
'image_url': {
'url': 'https://example.com/image1.jpg',
'url': 'https://example.com/image2.jpg' # ❌ WRONG: duplicate key overwrites, or attempting to pass multiple URLs in one dict
}
},
{'type': 'text', 'text': 'What is in these images?'}
]
}
]
)
print(response.choices[0].message.content) import os
from openai import OpenAI
client = OpenAI(api_key=os.environ.get('OPENAI_API_KEY'))
# FIXED: Each image as its own content dict in the array
response = client.chat.completions.create(
model='gpt-4o',
messages=[
{
'role': 'user',
'content': [
{
'type': 'image_url',
'image_url': {'url': 'https://example.com/image1.jpg'} # ✅ First image
},
{
'type': 'image_url',
'image_url': {'url': 'https://example.com/image2.jpg'} # ✅ Second image (separate dict)
},
{
'type': 'text',
'text': 'What is in these images?' # ✅ Text prompt after images
}
]
}
]
)
print(response.choices[0].message.content) Workaround
If you cannot immediately restructure your code, build the content array dynamically by iterating over image URLs and appending each as a separate dict: content = [{'type': 'image_url', 'image_url': {'url': url}} for url in image_urls] + [{'type': 'text', 'text': prompt}]. This avoids hardcoding and makes the correct structure explicit.
Prevention
Enforce content array structure via Pydantic validation before sending to OpenAI. Define a strict schema: images must be a List[ImageUrlContent] where ImageUrlContent = {'type': Literal['image_url'], 'image_url': {'url': HttpUrl}}. Use a helper function build_vision_message(image_urls: List[str], text: str) that always returns the correct structure, eliminating manual dict assembly.