Submission checklist
Package (Required)
Related Issues / PRs
No response
Reproduction Steps / Example Code (Python)
"""
Repro: langchain-openai silently swallows Responses API failure events.
pip install langchain-openai httpx
No API key or network needed (httpx.MockTransport). Reproduces on
langchain-openai 1.1.12 and 1.4.0.
The Responses API has four terminal stream events. The streaming converter
handles `response.completed` and `response.incomplete` (stamping `status`
into response_metadata), but `response.failed` and `error` fall through the
converter's `else` branch and are silently discarded — and a stream that
ends with NO terminal event at all is likewise returned as if successful.
"""
import json
import httpx
from langchain_openai import ChatOpenAI
_seq = 0
def sse(event_type: str, data: dict) -> bytes:
global _seq
data["sequence_number"] = _seq
_seq += 1
return f"event: {event_type}\ndata: {json.dumps(data)}\n\n".encode()
def base_frames() -> list[bytes]:
"""response.created, a message item, one text delta — no terminal event."""
envelope = {
"id": "resp_fake", "object": "response", "created_at": 1753246000,
"model": "gpt-4.1", "status": "in_progress", "output": [],
"parallel_tool_calls": False, "tool_choice": "auto", "tools": [],
}
return [
sse("response.created", {"type": "response.created", "response": envelope}),
sse("response.output_item.added", {
"type": "response.output_item.added", "output_index": 0,
"item": {"type": "message", "id": "msg_1", "role": "assistant",
"status": "in_progress", "content": []},
}),
sse("response.output_text.delta", {
"type": "response.output_text.delta", "item_id": "msg_1",
"output_index": 0, "content_index": 0, "delta": "The answer", "logprobs": [],
}),
]
FAILED_EVENT = sse("response.failed", {
"type": "response.failed",
"response": {
"id": "resp_fake", "object": "response", "created_at": 1753246000,
"model": "gpt-4.1", "status": "failed", "output": [],
"parallel_tool_calls": False, "tool_choice": "auto", "tools": [],
"error": {"code": "server_error",
"message": "The model failed to generate a response."},
},
})
def run(label: str, frames: list[bytes]) -> None:
def handler(request: httpx.Request) -> httpx.Response:
return httpx.Response(
200, headers={"content-type": "text/event-stream"},
content=b"".join(frames),
)
llm = ChatOpenAI(
model="gpt-4.1",
use_responses_api=True,
api_key="sk-fake",
http_client=httpx.Client(transport=httpx.MockTransport(handler)),
)
full = None
for chunk in llm.stream("what is 2+2?"):
full = chunk if full is None else full + chunk
print(f"--- {label}")
print(f" raised: nothing")
print(f" text: {full.text!r}")
print(f" 'status' in response_metadata: {'status' in full.response_metadata}"
f" (value: {full.response_metadata.get('status')!r})")
print(f" error surfaced anywhere: "
f"{'error' in full.response_metadata or 'error' in full.additional_kwargs}")
# A) stream dies with no terminal event -> looks like a successful reply
run("A: stream ends with NO terminal event", base_frames())
# B) stream ends with response.failed (error payload included) -> identical:
# event silently dropped, error payload lost, no exception
run("B: stream ends with response.failed", base_frames() + [FAILED_EVENT])
# C) control: response.completed IS handled -> status stamped, proving the
# metadata channel exists for terminal events
completed = sse("response.completed", {
"type": "response.completed",
"response": {
"id": "resp_fake", "object": "response", "created_at": 1753246000,
"model": "gpt-4.1", "status": "completed",
"parallel_tool_calls": False, "tool_choice": "auto", "tools": [],
"error": None, "incomplete_details": None,
"output": [{"type": "message", "id": "msg_1", "role": "assistant",
"status": "completed",
"content": [{"type": "output_text", "text": "The answer",
"annotations": []}]}],
"usage": {"input_tokens": 10, "output_tokens": 3, "total_tokens": 13,
"input_tokens_details": {"cached_tokens": 0},
"output_tokens_details": {"reasoning_tokens": 0}},
},
})
Error Message and Stack Trace (if applicable)
Description
We hit this in production with a LangGraph agent on ChatOpenAI (use_responses_api=True). Mid-generation, OpenAI's backend killed the stream. Instead of an error, ainvoke returned a normal-looking AIMessage containing only the partial output (reasoning blocks, no text). Our graph treated it as a final answer and the user got
silence. It took us two days to trace, because nothing raised and nothing was logged at any layer.
The cause (we think) is in _convert_responses_chunk_to_generation_chunk in langchain_openai/chat_models/base.py. The Responses API has four terminal stream events. The converter handles response.completed and response.incomplete (that branch stamps status and usage into response_metadata), but response.failed and error fall through the final else and are dropped, including the error payload OpenAI sent (response.error with code and message). A stream that ends with no terminal event at all (connection died) is likewise returned as if it finished normally. In all these cases the returned message is indistinguishable from a deliberate short answer unless you know to check for a missing status key.
We expected an exception, or at a minimum a status: "failed" plus the error payload stamped into response_metadata so callers can detect it.
What actually happened was just an AI message
The repro runs as-is with no API key (mock transport). Scenario A is the no-terminal-event case, B is response.failed, C is the response.completed controlshowing the metadata channel works when the event is handled. Reproduced on langchain-openai 1.1.12 and 1.4.0 (langchain-core 1.5.0), Python 3.12.
System Info
System Information
OS: Windows
OS Version: 10.0.26200
Python Version: 3.12.0 (tags/v3.12.0:0fb18b0, Oct 2 2023, 13:03:39) [MSC v.1935 64 bit (AMD64)]
Package Information
langchain_core: 1.5.0
langsmith: 0.10.10
langchain_openai: 1.4.0
langchain_protocol: 0.0.18
Optional packages not installed
deepagents
deepagents-cli
Other Dependencies
anyio: 4.14.2
distro: 1.9.0
httpx: 0.28.1
jsonpatch: 1.33
openai: 2.47.0
orjson: 3.11.9
packaging: 26.2
pydantic: 2.13.4
pyyaml: 6.0.3
requests: 2.34.2
requests-toolbelt: 1.0.0
sniffio: 1.3.1
tenacity: 9.1.4
tiktoken: 0.13.0
typing-extensions: 4.16.0
uuid-utils: 0.17.0
websockets: 16.1.1
xxhash: 3.8.1
zstandard: 0.25.0
Submission checklist
Package (Required)
Related Issues / PRs
No response
Reproduction Steps / Example Code (Python)
Error Message and Stack Trace (if applicable)
Description
We hit this in production with a LangGraph agent on ChatOpenAI (use_responses_api=True). Mid-generation, OpenAI's backend killed the stream. Instead of an error, ainvoke returned a normal-looking AIMessage containing only the partial output (reasoning blocks, no text). Our graph treated it as a final answer and the user got
silence. It took us two days to trace, because nothing raised and nothing was logged at any layer.
The cause (we think) is in _convert_responses_chunk_to_generation_chunk in langchain_openai/chat_models/base.py. The Responses API has four terminal stream events. The converter handles response.completed and response.incomplete (that branch stamps status and usage into response_metadata), but response.failed and error fall through the final else and are dropped, including the error payload OpenAI sent (response.error with code and message). A stream that ends with no terminal event at all (connection died) is likewise returned as if it finished normally. In all these cases the returned message is indistinguishable from a deliberate short answer unless you know to check for a missing status key.
We expected an exception, or at a minimum a status: "failed" plus the error payload stamped into response_metadata so callers can detect it.
What actually happened was just an AI message
The repro runs as-is with no API key (mock transport). Scenario A is the no-terminal-event case, B is response.failed, C is the response.completed controlshowing the metadata channel works when the event is handled. Reproduced on langchain-openai 1.1.12 and 1.4.0 (langchain-core 1.5.0), Python 3.12.
System Info
System Information
Package Information
Optional packages not installed
Other Dependencies