Area: sdk — streaming reliability. Surfaced in review of #203, deliberately not changed there.
The fetch-based stream transport treats any 4xx as terminal: parseErrorResponse (clients/ts/src/errors.ts) marks only >= 500 retryable, and _attempt ends the stream on a non-retryable status. That rule is right for the cases it was written for — a rejected token or a missing table will not be talked round by repeating the request — and it matches the REST path.
429 Too Many Requests is the exception. It is explicitly a retry-later status, usually carrying Retry-After. WaveHouse itself never emits it on /v1/stream (the endpoint is ungated), so it comes from a fronting proxy, CDN, or API gateway — exactly the deployments this transport is meant to work behind.
The failure mode is unpleasant: a brief upstream blip drops many streams at once, they all reconnect, the proxy rate-limits the burst, and every client takes a terminal HTTP_429 and stays dead until the page reloads or the process restarts. The jittered backoff added in #203 exists to avoid exactly that thundering herd, and a terminal 429 defeats it at the last step.
Proposal
Treat 429 as retryable on the stream path, and honor Retry-After as a backoff floor — the mechanism already exists (_retryFloorMs, fed today only by an SSE retry: field, clamped to MAX_BACKOFF_MS).
- Parse
Retry-After in both forms: delta-seconds and an HTTP-date.
- Clamp as
retry: already is, so a hostile or fat-fingered value can't strand a stream.
- Keep every other
4xx terminal.
The REST path already special-cases 503 + Retry-After (clients/ts/src/http.ts), so this is consistent with how the SDK treats the other "come back later" status rather than a new concept.
Worth deciding explicitly
Whether REST should get the same treatment for 429. Today a REST 429 is non-retryable too. Arguably the same argument applies, but the blast radius differs: a REST call returns an error the caller can act on, whereas a dead stream is silent until someone notices data stopped arriving.
Acceptance
Related: #203 (introduced the terminal-4xx rule and the backoff), #465 (the other streaming-load issue).
Area: sdk — streaming reliability. Surfaced in review of #203, deliberately not changed there.
The
fetch-based stream transport treats any4xxas terminal:parseErrorResponse(clients/ts/src/errors.ts) marks only>= 500retryable, and_attemptends the stream on a non-retryable status. That rule is right for the cases it was written for — a rejected token or a missing table will not be talked round by repeating the request — and it matches the REST path.429 Too Many Requestsis the exception. It is explicitly a retry-later status, usually carryingRetry-After. WaveHouse itself never emits it on/v1/stream(the endpoint is ungated), so it comes from a fronting proxy, CDN, or API gateway — exactly the deployments this transport is meant to work behind.The failure mode is unpleasant: a brief upstream blip drops many streams at once, they all reconnect, the proxy rate-limits the burst, and every client takes a terminal
HTTP_429and stays dead until the page reloads or the process restarts. The jittered backoff added in #203 exists to avoid exactly that thundering herd, and a terminal 429 defeats it at the last step.Proposal
Treat
429as retryable on the stream path, and honorRetry-Afteras a backoff floor — the mechanism already exists (_retryFloorMs, fed today only by an SSEretry:field, clamped toMAX_BACKOFF_MS).Retry-Afterin both forms: delta-seconds and an HTTP-date.retry:already is, so a hostile or fat-fingered value can't strand a stream.4xxterminal.The REST path already special-cases
503+Retry-After(clients/ts/src/http.ts), so this is consistent with how the SDK treats the other "come back later" status rather than a new concept.Worth deciding explicitly
Whether REST should get the same treatment for
429. Today a REST429is non-retryable too. Arguably the same argument applies, but the blast radius differs: a REST call returns an error the caller can act on, whereas a dead stream is silent until someone notices data stopped arriving.Acceptance
429on a stream is retryable and re-dialsRetry-After(seconds and HTTP-date) sets the backoff floor, clamped to 30s4xxstays terminal — pinned by a test, since this is the rule protecting against retrying a rejected token foreverdocs/src/content/docs/sdk/reference.mdand the "4xx is terminal" prose insdk/streaming.mdRelated: #203 (introduced the terminal-4xx rule and the backoff), #465 (the other streaming-load issue).