You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Confirm this is an issue with the Python library and not an underlying OpenAI API
This is an issue with the Python library
Describe the bug
In some occasions while using the Completion API with Structured Outputs, the SDK fails and returns a ValidationError:
ValidationError: 1 validation error for RawResponse
Invalid JSON: EOF while parsing a value at line 1 column 600 [type=json_invalid, input_value=' ... ', input_type=str]
For further information visit https://errors.pydantic.dev/2.9/v/json_invalid
This does not happen every time, but we use it in a production service and this unpredictable behavior is hard to prevent.
To Reproduce
Create a Pydantic model
Instantiate an OpenAI client
Use the method OpenAI.beta.chat.completions.parse(...) with the following arguments
ValidationError: 1 validation error for RawResponse
Invalid JSON: EOF while parsing a value at line 1 column 600 [type=json_invalid, input_value=' ... ', input_type=str]
For further information visit https://errors.pydantic.dev/2.9/v/json_invalid
Code snippets
No response
OS
debian:bullseye-slim
Python version
CPython 3.10.8
Library version
openai 1.48.0
Reacted by Hernán Vignolo, Nicolás Aldecoa Rodrigo, Pablo, Mauro Corvaro, Victoria Shanly, Jake Schwartz, Giorgio Demarchi, Atsushi Shirafuji, Mikhail, Min Hee Jo and 3 more
Hi, thanks for the quick reply! Sadly I can't provide a full script for privacy reasons but I'll be happy to share any information you need for identifying the issue.
Here's the traceback:
File "/app/src/core/modules/emiGPT/core/openai_chat_api.py", line 95, in _get_response
completion = self._client.beta.chat.completions.parse(
File "/opt/venv/lib/python3.10/site-packages/openai/resources/beta/chat/completions.py", line 145, in parse
return _parse_chat_completion(
File "/opt/venv/lib/python3.10/site-packages/openai/lib/_parsing/_completions.py", line 110, in parse_chat_completion
"parsed": maybe_parse_content(
File "/opt/venv/lib/python3.10/site-packages/openai/lib/_parsing/_completions.py", line 161, in maybe_parse_content
return _parse_content(response_format, message.content)
File "/opt/venv/lib/python3.10/site-packages/openai/lib/_parsing/_completions.py", line 221, in _parse_content
return cast(ResponseFormatT, model_parse_json(response_format, content))
File "/opt/venv/lib/python3.10/site-packages/openai/_compat.py", line 166, in model_parse_json
return model.model_validate_json(data)
File "/opt/venv/lib/python3.10/site-packages/pydantic/main.py", line 625, in model_validate_json
return cls.__pydantic_validator__.validate_json(json_data, strict=strict, context=context)
pydantic_core._pydantic_core.ValidationError: 1 validation error for RawResponse
Invalid JSON: EOF while parsing a value at line 1 column 600 [type=json_invalid, input_value=' ... ', input_type=str]
For further information visit https://errors.pydantic.dev/2.9/v/json_invalid
Please let me know if there's anything else you need.
From seeing the logs of our application I understand the call to client.beta.chat.completions.parse(...) resulted in an exception, thus it gave no result from which to extract a request_id. 😞
How complex are the models you're putting together? I wonder if it's just a token limit or something?
e.g. here's a simple Pydantic model from a test script I was playing around with:
class UserInformation(BaseModel):
name: Optional[str] = Field(description="Name of the user")
email: Optional[str] = Field(description="Email address of the user")
phone: Optional[str] = Field(description="Phone number of the user. Store this in the format '+1 123-456-7890'")
title_role: Optional[str] = Field(description="The Title or Role of the user at their company")
company_name: Optional[str] = Field(description="Name of the company the user works for")
which explodes into:
{
"properties": {
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Name of the user",
"title": "Name"
},
"email": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Email address of the user",
"title": "Email"
},
"phone": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Phone number of the user. Store this in the format +1 123-456-7890",
"title": "Phone"
},
"title_role": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "The Title or Role of the user at their company",
"title": "Title Role"
},
"company_name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Name of the company the user works for",
"title": "Company Name"
}
},
"required": [
"name",
"email",
"phone",
"title_role",
"company_name"
],
"title": "UserInformation",
"type": "object"
}
A fairly complex model feels like it would generate quite a few tokens which might get missed/misinterpreted. I wonder if a simplified json blob for the response_format would help things?
My schema is only slightly more complex than your example, very similar, but wrapped in an array allowing the model to return multiple entries for each prompt.
Output token lengths are fairly reasonable, but I assume there must be pathological cases.
@DeterjoSimon
You're correct. But since we've been experiencing a lot of hallucinations in our responses (not matching a schema we defined) we decided to include raw answer and in case of parsing error do it on our own using some heuristic:
Today I have found in our telemetry something which could help finding a root cause of this bug. It seems that the input for parsing an answer into Pydantic schema is just full of 0x0a (line feed characters)
{
"errors": [
{
"type": "json_invalid",
"loc": "()",
"msg": "Invalid JSON: EOF while parsing a value at line 1804 column 0",
"input": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
"ctx": { "error": "EOF while parsing a value at line 1804 column 0" },
"url": "https://errors.pydantic.dev/2.10/v/json_invalid"
}
],
"title": "FinalResponse",
"json": "[{\"type\":\"json_invalid\",\"loc\":[],\"msg\":\"Invalid JSON: EOF while parsing a value at line 1804 column 0\",\"input\":\"\\n \\n (more newline chars..."
}
Still seeing this on openai-python latest. The 'EOF while parsing' surfaces when the model truncates mid-output (length stop_reason) and the parse helper tries strict JSON.
Two patterns that helped: catch ValidationError separately from BadRequestError so you can distinguish a truncation retry-with-larger-max-tokens from a real schema problem, and feed the partial-JSON tail through a salvage step before re-prompting.
Would be nice if parse() exposed the partial-parsed value alongside the error so callers can decide.
One practical workaround is to separate transport diagnostics from the strict Pydantic parse step. If the HTTP request succeeds but parse() later raises pydantic.ValidationError, you can still capture the request id by using the raw-response wrapper and only then parsing:
frompydanticimportValidationErrorraw=client.beta.chat.completions.with_raw_response.parse(
model=model,
messages=messages,
response_format=MyPydanticModel,
max_tokens=1500, # or max_completion_tokens, depending on the model/API path
)
request_id=raw.headers.get("x-request-id")
try:
completion=raw.parse()
exceptValidationErrorasexc:
logger.warning("structured output parse failed", extra={"request_id": request_id})
raise
For production handling, I would also avoid retrying every ValidationError the same way. EOF while parsing is usually consistent with the model output being cut off before valid JSON closed, so treat that as a retry with a larger output budget or a smaller schema. A real schema mismatch should be handled separately because increasing max_tokens will not fix it.
If you need to classify it before the SDK parses into the Pydantic model, the more debuggable path is to call the non-parse chat completion with the equivalent JSON schema response format, inspect choices[0].finish_reason for length, log response._request_id, then run MyPydanticModel.model_validate_json(choice.message.content) yourself. That gives you a clean split between API completion, truncation, and local validation.
Always check finish_reason == 'length' first — this is the most common cause of the EOF error
The choice.message.parsed being None with finish_reason == 'stop' is the rarer transient case
The fix PRs (#linked above) will handle this at the SDK level, but this gives a production-safe path in the meantime
Also worth noting: if you're using streaming + stream.get_final_message().choices[0].message.parsed, truncation during streaming can similarly produce None or raise here — same finish_reason check applies.
Thanks everyone for sharing your examples! When a response doesn’t contain valid JSON, .parse() raises a validation error so your application can choose whether to retry or use a fallback. We’re closing this as expected SDK behavior. If you’re still receiving whitespace-only responses with the latest SDK and your current model, please share a minimal example with sensitive data removed and we can reopen to investigate.
Activity
RobertCraigie commentedon Sep 30, 2024
Thanks for the report, it looks like your example script isn't fully complete, could you share a full script?
marinomaria commentedon Sep 30, 2024
Hi, thanks for the quick reply! Sadly I can't provide a full script for privacy reasons but I'll be happy to share any information you need for identifying the issue.
Here's the traceback:
Please let me know if there's anything else you need.
RobertCraigie commentedon Sep 30, 2024
Could you share a request ID from a failing request? https://github.com/openai/openai-python#request-ids
marinomaria commentedon Sep 30, 2024
From seeing the logs of our application I understand the call to
client.beta.chat.completions.parse(...)resulted in an exception, thus it gave no result from which to extract arequest_id. 😞RobertCraigie commentedon Sep 30, 2024
ahhhh right sorry, if you don't already have debug logging enabled, could you enable it? https://github.com/openai/openai-python#logging that should show a request ID in the logs
marinomaria commentedon Sep 30, 2024
Sure thing!
jonomillin commentedon Nov 13, 2024
Hi there! Any updates here?
FYI - same thing happening for me, probably 50% of the time:
I've solved this in the meantime with a tenacity retry, but it's adding latency and calls which isn't ideal...
Tom-OCRT commentedon Dec 4, 2024
I'm experiencing the same issue.
I'm considering consuming the API directly myself as this is a bit of a pain.
Is there an intention to address this bug?
jonomillin commentedon Dec 4, 2024
How complex are the models you're putting together? I wonder if it's just a token limit or something?
e.g. here's a simple Pydantic model from a test script I was playing around with:
which explodes into:
A fairly complex model feels like it would generate quite a few tokens which might get missed/misinterpreted. I wonder if a simplified json blob for the
response_formatwould help things?Tom-OCRT commentedon Dec 4, 2024
My schema is only slightly more complex than your example, very similar, but wrapped in an array allowing the model to return multiple entries for each prompt.
Output token lengths are fairly reasonable, but I assume there must be pathological cases.
Refactor condition to leverage truthiness for message.content in parsing
DeterjoSimon commentedon Jan 16, 2025
Did anybody find a fix to this issue? This issue only recently appear for me.
fix(structured outputs): avoid parsing empty empty content (#2023)
rjoshi commentedon Jan 16, 2025
Thanks @RobertCraigie for merging the PR! @DeterjoSimon just pushed the fix, should be available in next release!
4 remaining items
ms-86 commentedon Jan 17, 2025
@DeterjoSimon
You're correct. But since we've been experiencing a lot of hallucinations in our responses (not matching a schema we defined) we decided to include raw answer and in case of parsing error do it on our own using some heuristic:
fix(structured outputs): avoid parsing empty empty content (#2023)
ms-86 commentedon Jan 22, 2025
Today I have found in our telemetry something which could help finding a root cause of this bug. It seems that the input for parsing an answer into Pydantic schema is just full of 0x0a (line feed characters)
{ "errors": [ { "type": "json_invalid", "loc": "()", "msg": "Invalid JSON: EOF while parsing a value at line 1804 column 0", "input": "\n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n \n \n\n", "ctx": { "error": "EOF while parsing a value at line 1804 column 0" }, "url": "https://errors.pydantic.dev/2.10/v/json_invalid" } ], "title": "FinalResponse", "json": "[{\"type\":\"json_invalid\",\"loc\":[],\"msg\":\"Invalid JSON: EOF while parsing a value at line 1804 column 0\",\"input\":\"\\n \\n (more newline chars..." }fix(structured outputs): avoid parsing empty empty content (#2023)
fix(structured outputs): avoid parsing empty empty content (#2023)
fix(structured outputs): avoid parsing empty empty content (#2023)
valenradovich commentedon Mar 18, 2025
Is this issue fixed? I'm having it and I don't know if it's because i'm using OpenAI from Azure or something like that
Fix: Catch ValidationError in beta.chat.completions.parse to prevent …
fix(structured outputs): avoid parsing empty empty content (#2023)
MukundaKatta commentedon May 10, 2026
Still seeing this on openai-python latest. The 'EOF while parsing' surfaces when the model truncates mid-output (length stop_reason) and the parse helper tries strict JSON.
Two patterns that helped: catch ValidationError separately from BadRequestError so you can distinguish a truncation retry-with-larger-max-tokens from a real schema problem, and feed the partial-JSON tail through a salvage step before re-prompting.
Would be nice if parse() exposed the partial-parsed value alongside the error so callers can decide.
diva0078 commentedon May 11, 2026
One practical workaround is to separate transport diagnostics from the strict Pydantic parse step. If the HTTP request succeeds but
parse()later raisespydantic.ValidationError, you can still capture the request id by using the raw-response wrapper and only then parsing:For production handling, I would also avoid retrying every
ValidationErrorthe same way.EOF while parsingis usually consistent with the model output being cut off before valid JSON closed, so treat that as a retry with a larger output budget or a smaller schema. A real schema mismatch should be handled separately because increasingmax_tokenswill not fix it.If you need to classify it before the SDK parses into the Pydantic model, the more debuggable path is to call the non-parse chat completion with the equivalent JSON schema response format, inspect
choices[0].finish_reasonforlength, logresponse._request_id, then runMyPydanticModel.model_validate_json(choice.message.content)yourself. That gives you a clean split between API completion, truncation, and local validation.Quantum0uasar commentedon May 27, 2026
For anyone still hitting this before the fix PRs land, here's a defensive wrapper pattern that prevents the
ValidationErrorfrom bubbling unhandled:Key points:
finish_reason == 'length'first — this is the most common cause of the EOF errorchoice.message.parsedbeingNonewithfinish_reason == 'stop'is the rarer transient caseAlso worth noting: if you're using streaming +
stream.get_final_message().choices[0].message.parsed, truncation during streaming can similarly produceNoneor raise here — samefinish_reasoncheck applies.marcuswood-oai commentedon Oct 26, 2026
Thanks everyone for sharing your examples! When a response doesn’t contain valid JSON,
.parse()raises a validation error so your application can choose whether to retry or use a fallback. We’re closing this as expected SDK behavior. If you’re still receiving whitespace-only responses with the latest SDK and your current model, please share a minimal example with sensitive data removed and we can reopen to investigate.