fix(core): continue retryable failures after durable output - #45861
Merged
Conversation
6 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.
Summary
A retryable failure arriving after the assistant produced durable output had no recovery path. The pre-output retry gate requires
!outputStarted, and the continuation gate only matched interrupted-stream shapes (Transportread failures, incomplete streams). A provider that streams partial output and then reports a retryable error — an overloaded/rate-limit/server error event inside the stream, or any unrecognized failure — matched neither gate and durably failed the turn, even though the same failure before output would have retried.The continuation gate now also accepts any failure the retry policy considers retry-eligible:
isInterruptedStream(failure)and output started.isInterruptedStream(failure) || isRetryable(failure)and output started.isInterruptedStreamstays in the condition rather than being replaced: WebSocket read failures can carrydelivery: "accepted"or"rejected", which the retry policy rejects for full resends but which must keep taking the continuation path after durable output.Behavior notes
retry-afterfloors, the 15-minute cap, and the shared five-attempt budget with no policy changes. A mid-stream rate limit now waits out itsretry-afterbefore continuing.Verification
retry-afterexactly; unrecognized mid-stream provider failure continues with backoff.InvalidRequestfixture.bun typecheckand formatting pass inpackages/core.