Repository navigation
Wrapped stream error chunks bypass retry and can leave parent sessions waiting forever #21979
Description
Description
When a provider using the Responses streaming path returns a wrapped error chunk such as:
{"error":{"code":"","message":"","type":"server_error"},"request_id":""}OpenCode currently tries to validate it as a normal response.* stream event and fails with:
Type validation failedinvalid_union
This appears to happen before any terminal event like response.completed / response.incomplete is seen.
That makes the failure behave like a stream protocol failure before terminal event, but it is currently surfaced as a schema/type validation error instead of a retryable API/stream failure.
This is especially problematic for subagents/background tasks: the child stream can die without entering a proper terminal failed/completed state, while the parent keeps waiting for the child response indefinitely.
Based on local investigation:
- the Responses path only treats
response.completed/response.incompleteas terminal - wrapped error chunks are not normalized before schema validation
- existing retry/backoff is already wired for
APICallError -> MessageV2.APIError -> SessionRetry.retryable() - plugin hooks can modify request params/headers, but cannot intercept/normalize response stream chunks before validation
So the current behavior seems to be:
- provider sends wrapped error chunk in stream
- chunk is validated as normal
response.*event - union validation fails (
invalid_union) - failure does not enter the normal retryable API error path
- parent/subagent waiting logic may never get a proper terminal failure signal
Plugins
oh-my-opencode
OpenCode version
Observed on recent OpenCode builds using the Responses streaming path; local repo analysis was against anomalyco/opencode dev and a local installation that surfaced the bundled stack in the TUI worker.
Steps to reproduce
- Use a provider/model path that goes through the Responses streaming implementation.
- Have the upstream stream return a wrapped error chunk like:
{"error":{"code":"","message":"","type":"server_error"},"request_id":""}- Observe that OpenCode reports a
Type validation failed/invalid_unionerror instead of handling it like a retryable stream/API failure. - In subagent/background-task scenarios, observe that the parent can continue waiting because the child did not complete with a normal terminal event.
Screenshot and/or share link
Example error payload observed locally:
Type validation failed: Value: {"error":{"code":"","message":"","type":"server_error"},"request_id":""}
Error message: invalid_union ...
Operating System
Linux
Terminal
N/A
Expected behavior
Wrapped stream error chunks / premature stream failures before response.completed or response.incomplete should be treated as something like:
stream terminated before terminal event- or
stream ended without completed response
and then mapped into the existing retry/backoff flow (for example via APICallError with the correct retryability), instead of surfacing as raw schema validation failure.
That would make this behave more like retryable HTTP/provider failures and also ensure parent sessions/subagents receive a proper terminal failure signal.
Suggested direction
For the Responses streaming path:
- skip empty/meta-only frames
- detect wrapped error chunks such as
{ "error": { ... } } - normalize them before schema validation, or convert them directly into a retryable
APICallError - if the stream ends without
response.completed/response.incomplete, treat that as a stream termination failure rather than a generic validation error
This would avoid the invalid_union surface error and should also prevent parent agents from waiting forever on failed child streams.
Activity
github-actions commentedon Apr 11, 2026
This issue might be a duplicate of existing issues. Please check:
rate_limit_error/stream_read_errorchunks) bypassing the retry path and terminating sessions. The error shapes, affected code paths (openai-responses-language-model.ts,retry.ts), and root cause described there overlap significantly with this report.If this issue covers a distinct aspect (specifically the Responses streaming path + parent/subagent indefinite wait), please clarify the difference so maintainers can track it separately.
tossp commentedon Apr 11, 2026
Thanks — I agree there is substantial overlap with #21893 on the retry-bypass side.
The distinct part I wanted to capture here is the parent/subagent waiting behavior in the Responses streaming path.
When this happens inside a subagent/background task, the child can fail before any terminal stream outcome is observed, and the parent may continue waiting indefinitely for the child result.
So from my perspective:
I’m also adding the concrete error shape here because this one did not just stay internal — it surfaced all the way to the TUI as a raw validation error:
And the important detail is that this is not a normal
response.*terminal event — it is a wrapped error chunk arriving on the stream beforeresponse.completed/response.incomplete.So the user-visible behavior is:
invalid_unionIf maintainers prefer, I’m happy to treat this as a sub-case of #21893 and close this one — but I wanted to explicitly call out the subagent/background-task hang and the fact that the raw validation failure is surfacing at the UI layer.
tossp commentedon Apr 11, 2026
I have a more precise update after building a processor-level regression around the exact failing payload on
openai/gpt-5.4:{"error":{"code":"","message":"","type":"server_error"},"request_id":""}What the regression confirms:
UnknownErrorType validation failedinvalid_unionserver_errorMore precise framing:
One important nuance from testing:
UnknownErrorinstead of being translated into a proper controlled error typeSo the issue is broader than "a wrapped error chunk missed a schema branch" — the key problem is that a wrapped upstream server error can turn into a raw SDK validation error, and OpenCode does not currently translate that into safe session-level error behavior.
ririnto commentedon May 22, 2026
I have a more concrete repro shape for the parent/subagent waiting part of this issue, from a sanitized local log on OpenCode 1.15.7.
This happened with a subagent child session on an OpenAI model. The upstream stream emitted a wrapped overload error:
The important sequence was:
So in this observed case, the child did not visibly remain in retry for 30 minutes. Instead, the child stream failed and the child session moved through the cancel path, but the parent/sync task did not receive a clean terminal failure/completion signal. It waited until the 30-minute inactivity window and then created a fallback child session.
That makes the user-visible behavior slightly different from a pure infinite retry loop:
No local paths, project paths, API keys, request IDs, or user content are included here. Session IDs and model names above are intentionally masked.
I am going to try a small PR scoped to the two seams this seems to expose: wrapped provider stream errors becoming controlled retryable session errors, and child assistant terminal errors being propagated as task failures instead of empty successful task results.
github-actions commentedon Jul 22, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.