fix(session): retry transient transport failures - #38683
Open
literally-dan wants to merge 1 commit into
Open
Conversation
Contributor
|
The following comment was made by an LLM, it may be inaccurate: Based on my search, I found one potentially related PR: #34010 - fix(core): retry transient response stream errors
The searches for the specific issue numbers (#30611 and #30638) referenced in the PR description only returned the current PR itself, and the PR explicitly states it replaces #30638 which was closed by automated cleanup. The searches confirm there are no other open PRs with those issue references. |
literally-dan
force-pushed
the
transport-retries
branch
2 times, most recently
from
July 28, 2026 11:31
a0a1fa0 to
7ae8ac0
Compare
A dropped connection mid-turn failed the whole turn. Classify transport faults by walking the error cause chain, which is where they end up once the provider SDK wraps them, and retry them at most 5 times (2s, 4s, 8s, 16s, 30s) before surfacing. Previously only a top-level ECONNRESET was recognised, so the same fault arriving as `TypeError: fetch failed` or inside an APICallError was reported as an unknown error. Bun's native fetch is the reason ConnectionRefused is transient rather than permanent. It reports DNS failure, refused connection, TLS mismatch and unroutable network with that one code and one shared message, so treating it as permanent means a wifi flap, VPN reconnect or sleep/wake hard-fails the turn. Bounded retry serves both cases: a genuinely wrong baseURL still surfaces within about a minute. ENOTFOUND, ECONNREFUSED and the DNS codes stay permanent because Node and undici only emit them for a specific unresolvable endpoint. Retrying replays the request unchanged, and nothing rolled back what the failed attempt had already written, so a mid-stream drop left the truncated output in the transcript with the full replacement appended after it. Each attempt now tracks the parts it persisted and removes them before the next one runs. A tool that already started executing is different: deleting the part cannot undo what it did to the disk, so the turn refuses to replay at all past that point. Abort is also handled once rather than per branch. A cancel can surface as a transport-shaped failure when it races the body read, so any retryable classification is converted back into an abort when the turn is known to be cancelled. That replaces the ZlibError-only guard and covers the other transport branches, which previously would have retried. An APICallError wrapping a transport fault now keeps its response envelope. Short-circuiting to a bare network error discarded statusCode, responseHeaders and the body, which cost the retry-after hint and the only useful diagnostics. Also caps ZlibError, header-timeout and SSE-stream-timeout retries, which were already retryable but previously unbounded.
literally-dan
force-pushed
the
transport-retries
branch
from
July 28, 2026 14:39
7ae8ac0 to
91d08f0
Compare
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue for this PR
Closes #30611
This replaces #30638, which was closed by automated cleanup. I rebased the change on the current
devbranch, tightened the transport error handling, and limited retries for these failures.Type of change
What does this PR do?
Transport errors are often wrapped by the provider SDK, so checking only the top-level error misses failures such as dropped sockets and response timeouts.
MessageV2.fromErrornow walks a bounded cause chain and recognizes transient Node, Bun, and Undici transport errors. Missing hosts, refused endpoints, and socket setup failures remain terminal. A plainAbortErrorstill means the request was cancelled.Transport retries stop after five attempts. Existing retry behavior for rate limits and provider errors is unchanged.
How did you verify your code works?
bun test test/session/message-v2.test.ts test/session/retry.test.tspasses 107 tests.bun typecheckalso passes inpackages/opencode.Screenshots / recordings
Not applicable.
Checklist