Skip to content

fix(sdk): recover mid-stream provider 5xx/429 like severed connections - #1174

Open
nordicnode wants to merge 1 commit into
CodebuffAI:mainfrom
nordicnode:oss/recover-provider-5xx-midstream-1155
Open

fix(sdk): recover mid-stream provider 5xx/429 like severed connections#1174
nordicnode wants to merge 1 commit into
CodebuffAI:mainfrom
nordicnode:oss/recover-provider-5xx-midstream-1155

Conversation

@nordicnode

Copy link
Copy Markdown

Summary

  • a provider-reported 500/429 arriving mid-stream — the openai-compatible shim enqueues it as an error part with finishReason='error' — no longer ends the entire run with an error
  • extend classifyThrownStreamRecovery (already consulted for both the thrown-iterator and error-chunk shapes) so retryable APICallErrors (429, any 5xx) take the same capped recovery path as a severed body: note injected into the conversation, retry step forced, capped at MAX_CONSECUTIVE_STREAM_RECOVERIES
  • the recovery message names the HTTP status instead of claiming the connection dropped
  • client-error statuses (400/401/402/403) are deterministic — retrying cannot help — so they stay fatal and still propagate to the run's error handling, including the existing 402 rethrow
  • add regression tests: 500 and 429 recover with a status-naming message, a wrapped 503 behind a RetryError cause chain recovers, client-error statuses and post-cancel 5xx stay fatal

This addresses the run-killing part of #1155: the recoverable class was "the connection failed to speak" (isTransientNetworkError, a socket-level allowlist) and the fatal class was "the provider reported a failure", which is backwards for flaky endpoints, where both are the same transient event.

Validation

Re-run on the new main after the history rewrite:

  • bun test sdk/src/__tests__/stream-interruption.test.ts — 19 pass, 0 fail
  • bunx tsc --noEmit -p sdk/tsconfig.json clean

Refs #1155


Replaces #1162 — auto-closed when the repo history was force-pushed (the original commits no longer exist in the new history). Rebased onto the new main per the maintainer's request; picking the review up here.

A provider-reported 500/429 arriving mid-stream — the openai-compatible
shim enqueues it as an error part with finishReason='error' — was thrown
straight out of the stream and ended the entire run with an error. The
same underlying transient event surfacing as a severed body instead
took the capped recovery path (note injected into the conversation,
retry step forced, capped at MAX_CONSECUTIVE_STREAM_RECOVERIES).

The recoverable class was 'the connection failed to speak' and the
fatal class was 'the provider reported a failure', which is backwards
for flaky endpoints, where both are the same transient event.

Route retryable APICallErrors (429, any 5xx) through the same capped
recovery path with a message naming the HTTP status. Client-error
statuses (400/401/402/403) are deterministic — retrying cannot help —
so they stay fatal and still propagate to the run's error handling.

Refs CodebuffAI#1155
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant