Nanobot
Nanobot finds streamed provider errors can bypass retry policy
A Nanobot report says a server_error arriving after streamed content can skip the normal retry path. Proposed provider and dispatcher changes retry eligible Codex failures while narrowing the channel exception boundary.
HKUDS/nanobot issue #5454 is the inspected primary source: “Streaming providers: mid-stream server_error skips retry once content has streamed.” The related records below were inspected as supporting context rather than independent confirmation.
The facts
- HKUDS/nanobot issue #5454 was created 2026-08-20T11:33:24Z and was open at inspection; its title is “Streaming providers: mid-stream server_error skips retry once content has streamed.” - HKUDS/nanobot PR #5455 was created 2026-08-20T11:34:07Z and was open at inspection; its title is “fix(provider): retry Codex server_error.” - HKUDS/nanobot PR #5457 was created 2026-08-20T13:54:59Z and was open at inspection; its title is “fix(channels): scope dispatcher exception boundary to message processing.” - The repository reported 47231 stars, 723 open issues, default branch main, and last push 2026-08-20T19:10:54Z. - The latest tagged-release baseline checked was v0.3.0 published 2026-07-25T08:08:47Z.
What changed
HKUDS/nanobot issue #5454 reports: Bug Description The transient-error retry logic does not retry a Codex response.failed (server_error) event once any content or reasoning has already been streamed for that turn. It only retries when the failure happens before the first delta. Steps to Reproduce 1. configure nanobot with codex subscription 2. talk to nanobot through any channel (telegram, discord, webui, etc.) 3. wait until a request where OpenAI streams at least one content or reasoning delta, then emits a mid-stream response.failed event with {"type": "server_error",...} 4. the raw error surfaces directly in that channel, with no retry attempt Expected Behavior A server_error mid-stream should be retried like a timeout is: recover the stream segment (or suppress delta callbacks) and retry, instead of giving up on the first attempt just because content was already streamed. Relevant Logs nanobot Version 0.3.0 Python Version 3.12 Operating System Linux Channel / Platform Telegram LLM Provider OpenAI Configuration (Optional) Additional Context _No response_
HKUDS/nanobot PR #5455 reports: Fixes #5454 Summary add "server_error" to _TRANSIENT_ERROR_MARKERS in nanobot/providers/base.py, alongside the existing "server error" Scope: this fixes the case where server_error arrives before any content/reasoning has streamed for the turn. Root cause OpenAI's Codex provider raises a plain RuntimeError for a mid-stream response.failed event (openai_responses/parsing.py:511,800), with no structured status_code/error_kind — so is_transient_response() fell back to text matching and missed it (space vs underscore) this made the retry loop give up immediately instead of using its normal 1s/2s/4s backoff Verification [x] confirmed red before the fix: reverting the one-line marker addition makes test_codex_mid_stream_server_error_is_treated_as_transient fail [x] pytest -q [x] ruff check nanobot/providers/base.py tests/providers/test_openai_codex_provider.py
HKUDS/nanobot PR #5457 reports: Summary An unexpected error while processing one outbound message could stop ChannelManager._dispatch_outbound, the background task responsible for delivering outbound messages. Once the task stopped, no further messages were sent until the process restarted. Root Cause OutboundMessage is a standard dataclass, so its content: str type hint is not enforced at runtime. A malformed upstream event can therefore create a message with content=None. When _coalesce_stream_deltas combines that message with another stream delta, combined_content += next_msg.content raises a TypeError. _dispatch_outbound handled timeouts and cancellation, but it did not handle unexpected errors during message processing. As a result, the error escaped the loop and stopped the dispatcher. Changes Split message acquisition and message processing into separate exception-handling blocks. Preserve the existing timeout and cancellation handling during message acquisition. Log processing failures with the affected channel, chat ID, and traceback. Drop only the failed message and continue processing the queue. Keep consume_outbound() outside the new processing exception boundary so queue-level failures behave as before. Add three focused regression tests in tests/channels/test_channel_manager_delta_coalescing.py. Testing [x] Confirmed that a malformed message no longer stops the dispatcher. [x] Confirmed that the next valid message is still delivered. [x] Confirmed that cancellation still stops the dispatcher cleanly. [x] Reproduced the original TypeError against the unfixed code. [x] Full tests/channels/ suite passes: 221 passed, 1 skipped. [x] ruff check passes.
Why it matters
Partial output is not necessarily successful output. If retry policy changes once bytes have streamed, operators need a clear terminal state that distinguishes usable completion from interrupted delivery. The operator test is whether the system remains bounded and its receipts still describe the action after failure, retry or restart.
Current
The primary record was open when captured on 2026-08-21. The tagged-release baseline was v0.3.0 published 2026-07-25T08:08:47Z. Repository metadata, full source bodies, current pull requests and issues, releases, Google News, Hacker News, Lobsters, Metamesh and the rendered ClawCharts row were inspected. ClawCharts selected the subject; it did not prove the claim.
Evidence
The primary URL is https://github.com/HKUDS/nanobot/issues/5454. Supporting records are https://github.com/HKUDS/nanobot/pull/5455 and https://github.com/HKUDS/nanobot/pull/5457. Source bodies, timestamps and states are preserved in the daily evidence bundle. Test counts and reproductions remain attributed to their authors unless identified as independently rerun.
Source boundary
Open work is described as open, closed work as closed, and operator reports as reports. A pull request is evidence of proposed or reviewed direction, not proof of a shipped release. Search residue, package mirrors and historically published source spines were excluded.
Operator take
Record stream progress and terminal error separately, retry only when idempotency is safe, and keep channel-dispatch failures from collapsing the whole receive loop. Preserve a before-state receipt, make the smallest reversible change, and verify the original failure independently.
Caveat
Public project records are mutable. Status, scope and evidence can change after publication. This brief records the inspected state and does not authorize changes to a reader’s deployment.
Source inspected; source state, environment and release boundary remain explicit.