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

InvalidRequestError

openai.BadRequestError (HTTP 400): invalid_request_error

What this error means
OpenAI's GPT-4o vision API rejects your message when multiple image URLs are placed in a single content block instead of separate content items, or when image formats don't match API requirements.

Stack trace

traceback
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'}}
QUICK FIX
Restructure your messages content array so each image is its own dict with type='image_url' and url nested inside image_url, not flattened or combined.

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

1

Multiple images nested in a single content dict instead of separate dicts

✓ Fix

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'}]

2

Passing base64 image data instead of image_url objects

✓ Fix

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

3

Missing 'url' field in image_url dict: only passing 'image_url' key without nested url

✓ Fix

Use correct nested structure: {'type': 'image_url', 'image_url': {'url': 'https://...'}}: image_url must be a dict with a url key inside

4

Image URLs that are inaccessible or return non-image content types

✓ Fix

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

Broken - triggers the error
python
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)
Fixed - works correctly
python
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)
Each image now gets its own dict in the content array with the correct nested structure image_url={'url': '...'}, allowing OpenAI to parse multiple images in a single message without validation errors.

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.

Python 3.9+ · openai >=1.0.0 · tested on 1.x (April 2026)
Verified 2026-04 · gpt-4o, gpt-4o-mini
Verify ↗

Community Notes

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