Skip to content

Python: [Bug]: response_format with Pydantic model crashes on background (Responses API) polling #5145

@Laende

Description

@Laende

Description

When using response_format=<PydanticModel> together with background=True (Responses API background mode), the SDK crashes with a ValidationError during polling. On each poll iteration, the SDK constructs a ChatResponse and eagerly accesses .value, which calls _parse_structured_response_value() with the current .text. While the background response is still in-progress, .text is an empty string (""), causing model_validate_json("") to fail.

This also affects the dict schema path — json.loads("") raises JSONDecodeError which is wrapped in a ValueError. Both code paths in _parse_structured_response_value are broken for background responses.

Steps to Reproduce

See code below

Root Cause

In _parse_non_streaming_response (_agents.py:1034), the AgentResponse is constructed with value=response.value. This eagerly triggers the ChatResponse.value property (_types.py:2247), which calls _parse_structured_response_value().

When the response is still in-progress (has a continuation_token), self.text is "". The function _parse_structured_response_value (_types.py:1961) unconditionally calls response_format.model_validate_json(text) on the empty string, which fails.

Suggested Fix

Guard against empty text in _parse_structured_response_value or skip parsing when continuation_token is present:

def _parse_structured_response_value(text: str, response_format: Any | None) -> Any | None:
    if response_format is None:
        return None
    if not text:  # <-- Add this guard for in-progress background responses
        return None
    # ... rest of existing parsing logic

Current Workaround

Monkey-patch _parse_structured_response_value to guard against empty text:

import agent_framework._types as _af_types

_original_parse = _af_types._parse_structured_response_value

def _safe_parse(text, response_format):
    if not text:
        return None
    return _original_parse(text, response_format)

_af_types._parse_structured_response_value = _safe_parse

With this patch applied, response_format=<PydanticModel> works correctly with background=True.value returns None on in-progress polls and the parsed Pydantic instance once complete.

Code Sample

from pydantic import BaseModel, Field
from agent_framework import Message, AgentSession
from agent_framework.openai import OpenAIChatClient, OpenAIChatOptions

class CityList(BaseModel):
    """A list of cities."""
    cities: list[str] = Field(description="List of city names")
    country: str = Field(description="Country name")

client = OpenAIChatClient(...)

default_options = OpenAIChatOptions(
    response_format=CityList,
    background=True,
)
agent = client.as_agent(
    instructions="You are a helpful assistant.",
    name="extractor",
    default_options=default_options,
)

session = AgentSession()
messages = [Message(role="user", contents=["List 50 Norwegian cities."])]

# Crashes here
response = await agent.run(
    messages=messages,
    session=session,
)

# Poll for completion
while response.continuation_token is not None:
    await asyncio.sleep(2)
    response = await agent.run(
        session=session,
        options=OpenAIChatOptions(continuation_token=response.continuation_token),
    )
# response.value would work here, but we never get here

Error Messages / Stack Traces

---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
Cell In[38], line 38
     34 )
     35 
     36 session = AgentSession()
     37 messages = [Message(role="user", contents=["List 50 Norwegian cities."])]
---> 38 response = await agent_bg_structured.run(
     39     messages=messages,
     40     session=session,
     41 )

File ...\agent_framework\_agents.py:955, in RawAgent.run.<locals>._run_non_streaming()
    953 ctx = await _prepare_run_context()
    954 response = await self._call_chat_client(ctx, stream=False)
--> 955 return await self._parse_non_streaming_response(ctx, response)

File ...\agent_framework\_agents.py:1034, in RawAgent._parse_non_streaming_response(self, context, response)
   1020     raise AgentInvalidResponseException("Chat client did not return a response.")
   1022 await self._finalize_response(
   1023     response=response,
   1024     agent_name=context["agent_name"],
   (...)   1027     suppress_response_id=context["suppress_response_id"],
   1028 )
   1029 return AgentResponse(
   1030     messages=response.messages,
   1031     response_id=None if context["suppress_response_id"] else response.response_id,
   1032     created_at=response.created_at,
   1033     usage_details=response.usage_details,
-> 1034     value=response.value,
   1035     response_format=context["chat_options"].get("response_format"),
   1036     continuation_token=response.continuation_token,
   1037     raw_representation=response,
   1038     additional_properties=response.additional_properties,
   1039 )

File ...\agent_framework\_types.py:2247, in ChatResponse.value(self)
   2245     return self._value
   2246 if self._response_format is not None:
-> 2247     self._value = cast(ResponseModelT, _parse_structured_response_value(self.text, self._response_format))
   2248     self._value_parsed = True
   2249 return self._value

File ...\agent_framework\_types.py:1961, in _parse_structured_response_value(text, response_format)
   1959     return None
   1960 if isinstance(response_format, type) and issubclass(response_format, BaseModel):
-> 1961     return response_format.model_validate_json(text)
   1962 if isinstance(response_format, Mapping):
   1963     try:

File ...\pydantic\main.py:766, in BaseModel.model_validate_json(cls, json_data, strict, extra, context, by_alias, by_name)
    760 if by_alias is False and by_name is not True:
    761     raise PydanticUserError(
    762         'At least one of `by_alias` or `by_name` must be set to True.',
    763         code='validate-by-alias-and-name-false',
    764     )
--> 766 return cls.__pydantic_validator__.validate_json(
    767     json_data, strict=strict, extra=extra, context=context, by_alias=by_alias, by_name=by_name
    768 )

ValidationError: 1 validation error for CityList
  Invalid JSON: EOF while parsing a value at line 1 column 0 [type=json_invalid, input_value='', input_type=str]
    For further information visit https://errors.pydantic.dev/2.12/v/json_invalid

Package Versions

agent-framework-core: 1.0.0, agent-framework-openai: 1.0.0, fastapi[standard]: 0.135.3, pydantic: 2.12.5

Python Version

Python 3.14.3

Additional Context

No response

Metadata

Metadata

Labels

bugSomething isn't workingpython

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions