Repository navigation
Description
Summary
This is a follow-up to #25888.
#25888 added server_is_overloaded handling in ProviderError.parseStreamError(), which fixes the path where the stream error payload reaches that parser.
However, there is still another real failure path where the same provider payload is wrapped in an Error instance first, for example:
{
"type": "error",
"sequence_number": 2,
"error": {
"type": "service_unavailable_error",
"code": "server_is_overloaded",
"message": "Our servers are currently overloaded. Please try again later.",
"param": null
}
}When this payload is surfaced as new Error(JSON.stringify(payload)), it can still be persisted as an UnknownError and the session stops instead of retrying.
Problem observed
We observed failed assistant messages where OpenAI returned a stream error with:
error.type = "service_unavailable_error"error.code = "server_is_overloaded"
This is a transient provider overload condition and should be retryable.
But in the wrapped Error(JSON.stringify(...)) path, MessageV2.fromError() currently classifies it before ProviderError.parseStreamError() can inspect the payload.
Current flow:
case e instanceof Error:
return new NamedError.Unknown({ message: errorMessage(e) }, { cause: e }).toObject()
default:
const parsed = ProviderError.parseStreamError(e)As a result:
ProviderError.parseStreamError()is never reached.- The stream error becomes
NamedError.Unknown. SessionRetry.retryable()does not classify the nestederror.code = "server_is_overloaded"shape as retryable.- The session stops instead of retrying.
Why the latest code is still insufficient
The latest code already includes #25888, which added this in ProviderError.parseStreamError():
case "server_is_overloaded":
case "server_error":
return {
type: "api_error",
isRetryable: true,
...
}That fix works only if the payload reaches parseStreamError().
It does not cover the Error(JSON.stringify(...)) path because the e instanceof Error branch in MessageV2.fromError() returns UnknownError before the parser is attempted.
There is also a second gap in SessionRetry.retryable(): the JSON fallback recognizes some top-level overload-like codes, but it does not inspect the nested OpenAI/Codex stream shape:
{
"type": "error",
"error": {
"type": "service_unavailable_error",
"code": "server_is_overloaded"
}
}So even if such an error has already been persisted as an UnknownError, it is still not retried.
Expected behavior
OpenAI/Codex stream errors with nested overload codes should be retried when they are surfaced either as:
- plain stream error objects /
{ message: JSON.stringify(payload) } Error(JSON.stringify(payload))- already-persisted
UnknownErrormessages containing the same JSON payload
Suggested fix
A narrow fix would be:
- In
MessageV2.fromError(), attemptProviderError.parseStreamError(e)before converting genericErrorinstances intoUnknownError. - In
SessionRetry.retryable(), recognize the nested OpenAI/Codex overload shape:error.code = "server_is_overloaded"error.type = "service_unavailable_error"
- Keep the matching narrow to avoid retrying unrelated JSON error messages.
- Add regression tests for the wrapped
Error(JSON.stringify(...))stream error shape.
Local verification
I tested a local patch with regression coverage for the wrapped stream error path:
bun test --timeout 30000 test/session/retry.test.ts
bun run typecheckBoth passed locally.
Activity
github-actions commentedon May 7, 2026
This issue might be a duplicate of existing issues. Please check:
The analysis in this issue is detailed and appreciated — if you believe the wrapped
Error(JSON.stringify(...))path represents a distinct unaddressed gap not covered by those issues, please add a comment clarifying that distinction so maintainers can triage accordingly.Dandi007 commentedon May 7, 2026
Thanks for the duplicate check.
I agree this is related to #25884 / #25730 / #25886 / #25728, but I believe this issue tracks one additional path that those fixes do not cover.
The existing fixes handle the stream payload when it reaches
ProviderError.parseStreamError(), for example as{ message: JSON.stringify(payload) }.The gap here is specifically when the same stream payload is wrapped as an actual
Errorinstance:In the current
MessageV2.fromError()flow, this hits thee instanceof Errorbranch before thedefaultbranch can callProviderError.parseStreamError():So even after #25888 / #25886-style fixes, this wrapped
Error(JSON.stringify(...))path can still becomeUnknownErrorand bypass retry.I think the remaining fix is to attempt
ProviderError.parseStreamError(e)before converting genericErrorinstances toUnknownError, plus a regression test for the wrappedError(JSON.stringify(...))case.github-actions commentedon Jul 8, 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.