diff --git a/packages/opencode/src/provider/error.ts b/packages/opencode/src/provider/error.ts index 3877dcb7f304..d0511a1d5ded 100644 --- a/packages/opencode/src/provider/error.ts +++ b/packages/opencode/src/provider/error.ts @@ -123,7 +123,10 @@ export function parseStreamError(input: unknown): ParsedStreamError | undefined const responseBody = JSON.stringify(body) if (body.type !== "error") return - switch (body?.error?.code) { + const errorCode = typeof body?.error?.code === "string" ? body.error.code : "" + const errorType = typeof body?.error?.type === "string" ? body.error.type : "" + + switch (errorCode) { case "context_length_exceeded": return { type: "context_overflow", @@ -159,6 +162,20 @@ export function parseStreamError(input: unknown): ParsedStreamError | undefined responseBody, } } + + if ( + errorType === "service_unavailable_error" || + errorCode === "server_is_overloaded" || + errorCode.includes("unavailable") || + errorCode.includes("exhausted") + ) { + return { + type: "api_error", + message: typeof body?.error?.message === "string" ? body.error.message : "Provider is overloaded", + isRetryable: true, + responseBody, + } + } } export type ParsedAPICallError = diff --git a/packages/opencode/test/session/retry.test.ts b/packages/opencode/test/session/retry.test.ts index 105c772d9735..bbfaf83d594c 100644 --- a/packages/opencode/test/session/retry.test.ts +++ b/packages/opencode/test/session/retry.test.ts @@ -323,4 +323,27 @@ describe("session.message-v2.fromError", () => { expect(result.data.isRetryable).toBe(true) expect(SessionRetry.retryable(result)).toBe("An error occurred while processing your request.") }) + + test("converts OpenAI overloaded stream chunks to retryable APIError", () => { + const result = MessageV2.fromError( + { + message: JSON.stringify({ + 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, + }, + }), + }, + { providerID: ProviderID.make("openai") }, + ) + + expect(MessageV2.APIError.isInstance(result)).toBe(true) + if (!MessageV2.APIError.isInstance(result)) throw new Error("expected APIError") + expect(result.data.isRetryable).toBe(true) + expect(SessionRetry.retryable(result)).toBe("Our servers are currently overloaded. Please try again later.") + }) })