AI & Agent Dev Bug Sandbox logo
AI & Agent Dev Bug Sandbox
Back to Radar

ChatOpenAI.With_structured_output(Json_schema) Streaming Drops Response Headers

When using ChatOpenAI.with_structured_output with method='json_schema' and streaming, the response_metadata headers (e.g., x-request-id) are missing from the streamed AIMessageChunk objects, unlike plain streaming or AzureChatOpenAI.

mediumConfidence 70%LangchainAffected V0.3.34

Origin Analysis

In langchain-openai's structured output streaming implementation for method='json_schema', the final AIMessageChunk constructed from parsed tool call arguments does not copy response_metadata from the raw OpenAI API chunks, resulting in loss of headers.
1. Set up ChatOpenAI with streaming=True and include_response_headers=True. 2. Define a Pydantic model and call llm.with_structured_output(SimpleAnswer, method='json_schema').stream('Say hello'). 3. Observe that the streamed chunks' response_metadata lacks headers, while plain streaming (without structured output) includes them.

Fixing Code Block

from typing import Any, Iterator from langchain_openai import ChatOpenAI from langchain_core.messages import AIMessageChunk class ChatOpenAIWithHeaderPropagation(ChatOpenAI): """Hotfix: preserve response_metadata headers in streamed structured output.""" def _stream(self, messages, stop=None, run_manager=None, **kwargs): last_headers = getattr(self, "_last_headers", None) for chunk in super()._stream(messages, stop=stop, run_manager=run_manager, **kwargs): if chunk.response_metadata and "headers" in chunk.response_metadata: last_headers = chunk.response_metadata["headers"] elif last_headers and chunk.response_metadata is not None: chunk.response_metadata["headers"] = last_headers self._last_headers = last_headers yield chunk
This subclass overrides _stream to capture headers from any chunk that contains them and reattach them to subsequent chunks that lack them. This ensures that the metadata is present on all streamed chunks, including those produced by the structured output wrapper, because the wrapper ultimately calls this _stream method.

Edge Case Audit

This hotfix only works if the structured output wrapper does not strip metadata after the chunks are yielded. If headers are still missing, a library-level fix is required in the structured output parser to copy response_metadata from raw chunks to the final parsed AIMessage. The monkey patch may break with future versions or if the _stream signature changes. Rollback: remove the subclass and use original ChatOpenAI.

Ecosystem Topology