Bug Description
Responses streaming can crash in the OpenAI SDK when an OpenAI-compatible provider emits valid response.output_item.done events, then sends a terminal response.completed whose response.output is null.
Hermes already has recovery logic for the related response.output == [] shape from Codex-style Responses streams, but this null shape fails earlier: stream.get_final_response() tries to parse the terminal response and raises before Hermes can backfill the streamed output items.
This appears to be a variant of #5879 / #5689 / #5730: same class of provider compatibility issue, but with output is None rather than an empty list.
Steps to Reproduce
- Configure Hermes to use an OpenAI-compatible Responses endpoint that streams output items but returns terminal
response.output: null.
- Send a request that produces streamed output, for example a tool call.
- Observe stream events like:
response.created
response.in_progress
response.output_item.added
response.output_item.done
response.function_call_arguments.delta
response.function_call_arguments.done
response.completed
- The terminal
response.completed.response.output is null.
- Hermes calls
stream.get_final_response(), and the OpenAI SDK raises TypeError while parsing the final response.
I reproduced this against a custom OpenAI-compatible /v1/responses provider. A sanitized fake stream regression is enough to reproduce the Hermes-side failure: emit response.output_item.done, then response.completed with response.output = None, and have get_final_response() raise TypeError("'NoneType' object is not iterable").
Expected Behavior
Hermes should recover from already-streamed response.output_item.done events when the terminal response has missing/null output, just as it already recovers when the final output is an empty list.
Actual Behavior
The SDK raises before existing empty-output backfill logic can run:
TypeError: 'NoneType' object is not iterable
Observed Hermes logs include:
ERROR root: Non-retryable client error: 'NoneType' object is not iterable
WARNING root: Failed to generate context summary: 'NoneType' object is not iterable. Further summary attempts paused for 60 seconds.
A live traceback from the SDK path was:
File ".../openai/lib/streaming/responses/_responses.py", line 93, in __stream__
self.__final_response = parse_response(...)
File ".../openai/lib/_parsing/_responses.py", line 61, in parse_response
for output in response.output:
TypeError: 'NoneType' object is not iterable
Affected Component
- Agent Core (conversation loop, context compression, memory)
- Gateway (Telegram/Discord/Slack/WhatsApp)
- Configuration (config.yaml, .env, hermes setup)
Messaging Platform (if gateway-related)
Gateway-related; observed through gateway usage. The underlying bug is in the Responses stream handling and is platform-independent.
Debug Report
Not uploaded here because the captured request dump and local debug bundle include private provider configuration and conversation payloads. The relevant sanitized stream shape, logs, and traceback are included above.
Operating System
Alibaba Cloud Linux 3 (OpenAnolis Edition), kernel 5.10.134-19.2.al8.x86_64.
Python Version
Python 3.14.3 under uv run; existing local venv is Python 3.11.13.
Hermes Version
Tested from current origin/main at commit 37913d91.
hermes version output in the local environment:
Hermes Agent v0.9.0 (2026.4.13)
Python: 3.14.3
OpenAI SDK: 2.24.0
Additional Logs / Traceback (optional)
Non-retryable error (HTTP None): 'NoneType' object is not iterable
Non-retryable client error: 'NoneType' object is not iterable
Failed to generate context summary: 'NoneType' object is not iterable
Root Cause Analysis (optional)
The existing recovery logic collects response.output_item.done and text deltas, then backfills the final response only when get_final_response() returns a response object with output == [].
For providers that send terminal response.output = null, the OpenAI SDK parser raises inside get_final_response() while iterating response.output, so Hermes never reaches the existing empty-list recovery path. The same issue can affect both the main agent stream path in run_agent.py and the auxiliary Codex/Responses adapter in agent/auxiliary_client.py.
Proposed Fix (optional)
Recover generically from the stream events rather than provider-specific behavior:
- Capture terminal
response.completed / response.incomplete / response.failed response objects from the event stream before calling get_final_response().
- If terminal
output is missing/null/empty and streamed response.output_item.done events were collected, backfill response.output from those items.
- If no output items exist but text deltas were collected and no tool calls were streamed, synthesize a message output from text deltas.
- Catch the specific SDK
TypeError("'NoneType' object is not iterable") final-parse failure only when streamed output exists, then recover through the same backfill path.
- Add fake-stream regressions for both the main agent stream and auxiliary client paths.
I am opening a PR with this fix.
Bug Description
Responses streaming can crash in the OpenAI SDK when an OpenAI-compatible provider emits valid
response.output_item.doneevents, then sends a terminalresponse.completedwhoseresponse.outputisnull.Hermes already has recovery logic for the related
response.output == []shape from Codex-style Responses streams, but thisnullshape fails earlier:stream.get_final_response()tries to parse the terminal response and raises before Hermes can backfill the streamed output items.This appears to be a variant of #5879 / #5689 / #5730: same class of provider compatibility issue, but with
output is Nonerather than an empty list.Steps to Reproduce
response.output: null.response.completed.response.outputisnull.stream.get_final_response(), and the OpenAI SDK raisesTypeErrorwhile parsing the final response.I reproduced this against a custom OpenAI-compatible
/v1/responsesprovider. A sanitized fake stream regression is enough to reproduce the Hermes-side failure: emitresponse.output_item.done, thenresponse.completedwithresponse.output = None, and haveget_final_response()raiseTypeError("'NoneType' object is not iterable").Expected Behavior
Hermes should recover from already-streamed
response.output_item.doneevents when the terminal response has missing/null output, just as it already recovers when the final output is an empty list.Actual Behavior
The SDK raises before existing empty-output backfill logic can run:
Observed Hermes logs include:
A live traceback from the SDK path was:
Affected Component
Messaging Platform (if gateway-related)
Gateway-related; observed through gateway usage. The underlying bug is in the Responses stream handling and is platform-independent.
Debug Report
Not uploaded here because the captured request dump and local debug bundle include private provider configuration and conversation payloads. The relevant sanitized stream shape, logs, and traceback are included above.
Operating System
Alibaba Cloud Linux 3 (OpenAnolis Edition), kernel
5.10.134-19.2.al8.x86_64.Python Version
Python 3.14.3underuv run; existing local venv isPython 3.11.13.Hermes Version
Tested from current
origin/mainat commit37913d91.hermes versionoutput in the local environment:Additional Logs / Traceback (optional)
Root Cause Analysis (optional)
The existing recovery logic collects
response.output_item.doneand text deltas, then backfills the final response only whenget_final_response()returns a response object withoutput == [].For providers that send terminal
response.output = null, the OpenAI SDK parser raises insideget_final_response()while iteratingresponse.output, so Hermes never reaches the existing empty-list recovery path. The same issue can affect both the main agent stream path inrun_agent.pyand the auxiliary Codex/Responses adapter inagent/auxiliary_client.py.Proposed Fix (optional)
Recover generically from the stream events rather than provider-specific behavior:
response.completed/response.incomplete/response.failedresponse objects from the event stream before callingget_final_response().outputis missing/null/empty and streamedresponse.output_item.doneevents were collected, backfillresponse.outputfrom those items.TypeError("'NoneType' object is not iterable")final-parse failure only when streamed output exists, then recover through the same backfill path.I am opening a PR with this fix.