"Could not parse JSON body" error in following Structured Output example

Here is the code for the example, straight from https://platform.openai.com/docs/guides/structured-outputs?context=with_parse

    from pydantic import BaseModel
    model = 'gpt-4o-2024-08-06'

    class CalendarEvent(BaseModel):
        name: str
        date: str
        participants: List[str]

    completion = client.beta.chat.completions.parse(
        model=model,
        messages=[
            {"role": "system", "content": "Extract the event information."},
            {"role": "user", "content": "Alice and Bob are going to a science fair on Friday."},
        ],
        response_format=CalendarEvent
    )
    print(json.dumps(completion, indent=2))

Results in the following error:

openai.BadRequestError: Error code: 400 - {'error': {'message': "We could not parse the JSON body of your request. (HINT: This likely means you aren't using your HTTP library correctly. The OpenAI API expects a JSON payload, but what was sent was not valid JSON. If you have trouble figuring out how to fix this, please contact us through our help center at help.openai.com.)", 'type': 'invalid_request_error', 'param': None, 'code': None}}

There is unseen code, obviously.

Let me show you how to make a request and receive the data.

Prepare - create a client instance:

from openai import Client
client = Client()

Throw in some code for printing arbitrary Python nested objects

def print_nested_dict(d: dict, indent: int = 0) -> None:
    """Recursively prints dictionary keys and values, handling nested dictionaries and lists."""
    for key, value in d.items():
        prefix = ' ' * indent
        if value is None:
            continue  # Skip None values
        if isinstance(value, dict):
            print(f"{prefix}{key}:")
            print_nested_dict(value, indent + 2)
        elif isinstance(value, list):
            print(f"{prefix}{key}:")
            print_list(value, indent + 2)
        else:
            print(f"{prefix}{key}: {value}")

def print_list(lst: list, indent: int) -> None:
    """Recursively prints items in a list, handling nested dictionaries and lists."""
    for i, item in enumerate(lst):
        prefix = ' ' * indent
        if item is None:
            continue  # Skip None values
        if isinstance(item, dict):
            print(f"{prefix}{i}:")
            print_nested_dict(item, indent + 2)
        elif isinstance(item, list):
            print(f"{prefix}{i}:")
            print_list(item, indent + 2)
        else:
            print(f"{prefix}{i}: {item}")

Create your BaseModel class

from pydantic import BaseModel
model = 'gpt-4o-2024-08-06'

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]  # lower-case: built in typing of Python 3.9+

Make your API request

completion = client.beta.chat.completions.parse(
    model=model,
    messages=[
        {"role": "system", "content": "Extract the event information."},
        {"role": "user", "content":
         "Alice and Bob are going to a science fair on Friday."
         },
    ],
    response_format=CalendarEvent
)

Now, what do you do with that? The response from the openai python library is NOT a json, it is a pydantic model of objects and models itself.

You can dump out the response like this:

print("response dump: ", completion.model_dump() )

And see your full response Python objects from JSON:

response dump: {'id': 'chatcmpl-APi..', 'choices': [{'finish_reason': 'stop', 'index': 0, 'logprobs': None, 'message': {'content': '{"name":"Science Fair","date":"Friday","participants":["Alice","Bob"]}', 'refusal': None, 'role': 'assistant', 'audio': None, 'function_call': None, 'tool_calls': [], 'parsed': {'name': 'Science Fair', 'date': 'Friday', 'participants': ['Alice', 'Bob']}}}], 'created': 1730691646, 'model': 'gpt-4o-2024-08-06', 'object': 'chat.completion', 'service_tier': None, 'system_fingerprint': 'fp_159d8341cc', 'usage': {'completion_tokens': 17, 'prompt_tokens': 92, 'total_tokens': 109, 'completion_tokens_details': {'audio_tokens': None, 'reasoning_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}}

Or if only the assistant message is important to you, get that out:

completion_message_dict = completion.choices[0].message.parsed.model_dump()

And see what you have to work with then, again, not JSON string but Python:

>>>print( completion_message_dict )
{'name': 'Science Fair', 'date': 'Friday', 'participants': ['Alice', 'Bob']}

That function you didn’t ask for? Let’s have it print

>>>print_nested_dict(completion_message_dict)
name: Science Fair
date: Friday
participants:
  0: Alice
  1: Bob

So I’ve demonstrated making the call without error and making its response useful.

Code solution:

import json  # if you still need it
from typing import List  # for other types, like Optional, for Pydantic) 
from openai import Client
client = Client()

def print_nested_dict(d: dict, indent: int = 0) -> None:
    """Recursively prints dictionary keys and values, handling nested dictionaries and lists."""
    for key, value in d.items():
        prefix = ' ' * indent
        if value is None:
            continue  # Skip None values
        if isinstance(value, dict):
            print(f"{prefix}{key}:")
            print_nested_dict(value, indent + 2)
        elif isinstance(value, list):
            print(f"{prefix}{key}:")
            print_list(value, indent + 2)
        else:
            print(f"{prefix}{key}: {value}")

def print_list(lst: list, indent: int) -> None:
    """Recursively prints items in a list, handling nested dictionaries and lists."""
    for i, item in enumerate(lst):
        prefix = ' ' * indent
        if item is None:
            continue  # Skip None values
        if isinstance(item, dict):
            print(f"{prefix}{i}:")
            print_nested_dict(item, indent + 2)
        elif isinstance(item, list):
            print(f"{prefix}{i}:")
            print_list(item, indent + 2)
        else:
            print(f"{prefix}{i}: {item}")

from pydantic import BaseModel
model = 'gpt-4o-2024-08-06'

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]  # lower-case: built in typing of Python 3.9+

completion = client.beta.chat.completions.parse(
    model=model,
    messages=[
        {"role": "system", "content": "Extract the event information."},
        {"role": "user", "content":
         "Alice and Bob are going to a science fair on Friday."
         },
    ],
    response_format=CalendarEvent
)
print("response dump: ", completion.model_dump(), "\n----\n" )
completion_message_dict = completion.choices[0].message.parsed.model_dump()
print("response: ", completion_message_dict, "\n----\n" )
print_nested_dict(completion_message_dict)

I had been using “List” from “typing” module as follows:

from typing import List

Maybe that was the issue. Let me try this new code and get back to you. Thanks for your help.

The code you showed is not working. Something wrong with my environment. I am using python 3.12 on Ubuntu 24.04 and OpenAI version is 1.53.

The following does not work. Note, since I am using python 3.12, I got rid of “List” from “typing” and using “list”.

        class CalendarEvent(BaseModel):
            name: str
            date: str
            participants: list[str]
        client = OpenAI()
        completion = client.beta.chat.completions.parse(
            model=model,
            messages=[
                {"role": "system", "content": "Extract the event information."},
                {"role": "user", "content": "Alice and Bob are going to a science fair on Friday."},
            ],
            response_format=CalendarEvent
        )
        print(completion.model_dump())

I get the following error:

openai.BadRequestError: Error code: 400 - {'error': {'message': "We could not parse the JSON body of your request. (HINT: This likely means you aren't using your HTTP library correctly. The OpenAI API expects a JSON payload, but what was sent was not valid JSON. If you have trouble figuring out how to fix this, please contact us through our help center at help.openai.com.)", 'type': 'invalid_request_error', 'param': None, 'code': None}}

However, using the requests module with the same CalendarEvent class works. Note that instead of passing a BaseModel subclass, I am extracting the json schema from it and using that.

        class CalendarEvent(BaseModel):
            name: str
            date: str
            participants: list[str]
        resp = requests.post('https://api.openai.com/v1/chat/completions', headers={
            "Authorization": f'Bearer {APPENV["OPENAI_API_KEY"]}'
        }, json={
            "model": "gpt-4o-2024-08-06",
            "messages": [
                {
                    "role": "system",
                    "content": "Extract the event information."
                },
                {
                    "role": "user",
                    "content": "Alice and Bob are going to a science fair on Friday."
                }
            ],
            "response_format": {
                "type": "json_schema",
                "json_schema": {
                    "name": "whatever-man",
                    "schema": CalendarEvent.model_json_schema()   
                }
            }
        })
        print(resp.json())

Any ideas? Also, what is your environment? (Python version, OS version and OpenAI version).

Here’s an answer for you: not the latest versions of support libraries like Pydantic.

You may need to make a venv and follow the install versioning I note here:

Problems - and using bleeding-edige python versions.

Yeah, bleeding edge is not a good idea. I only installed python 3.12 after I had errors with python 3.8 earlier. Apparently that did not resolve my issues. So I am now back on 3.8. But let me now try with python 3.11.

Thanks for your help!