diff --git a/packages/opencode/src/provider/error.ts b/packages/opencode/src/provider/error.ts index 7318d8022..7a2850e7d 100644 --- a/packages/opencode/src/provider/error.ts +++ b/packages/opencode/src/provider/error.ts @@ -144,9 +144,12 @@ export function parseStreamError(input: unknown): ParsedStreamError | undefined const responseBody = JSON.stringify(body) if (body.type !== "error") return + const errorCode = typeof body?.error?.code === "string" ? body.error.code : "" + const errorType = typeof body?.error?.type === "string" ? body.error.type : "" - switch (body?.error?.code) { + switch (errorCode) { case "server_error": + case "server_is_overloaded": return { type: "api_error", message: typeof body?.error?.message === "string" ? body.error.message : "OpenAI server error", @@ -181,6 +184,14 @@ export function parseStreamError(input: unknown): ParsedStreamError | undefined responseBody, } } + if (errorType === "service_unavailable_error") { + return { + type: "api_error", + message: typeof body?.error?.message === "string" ? body.error.message : "OpenAI service unavailable", + isRetryable: true, + responseBody, + } + } } export type ParsedAPICallError = diff --git a/packages/opencode/test/provider/error.test.ts b/packages/opencode/test/provider/error.test.ts index 7716453e9..1bb2ba6f5 100644 --- a/packages/opencode/test/provider/error.test.ts +++ b/packages/opencode/test/provider/error.test.ts @@ -179,4 +179,24 @@ describe("provider stream error", () => { responseBody: JSON.stringify(input), }) }) + + test("marks OpenAI overloaded service unavailable events as retryable", () => { + const input = { + type: "error", + sequence_number: 3, + error: { + type: "service_unavailable_error", + code: "server_is_overloaded", + message: "Our servers are currently overloaded. Please try again later.", + param: null, + }, + } + + expect(parseStreamError(input)).toStrictEqual({ + type: "api_error", + message: input.error.message, + isRetryable: true, + responseBody: JSON.stringify(input), + }) + }) }) diff --git a/packages/opencode/test/session/retry.test.ts b/packages/opencode/test/session/retry.test.ts index 9ed7f2646..3fcc33d53 100644 --- a/packages/opencode/test/session/retry.test.ts +++ b/packages/opencode/test/session/retry.test.ts @@ -148,6 +148,30 @@ describe("session.retry.retryable", () => { expect(SessionRetry.retryable(error)).toBe(input.error.message) }) + test("retries OpenAI overloaded service unavailable stream events", () => { + const input = { + type: "error", + sequence_number: 3, + error: { + type: "service_unavailable_error", + code: "server_is_overloaded", + message: "Our servers are currently overloaded. Please try again later.", + param: null, + }, + } + const error = MessageV2.fromError(input, { providerID }) + + expect(error).toStrictEqual({ + name: "APIError", + data: { + message: input.error.message, + isRetryable: true, + responseBody: JSON.stringify(input), + }, + }) + expect(SessionRetry.retryable(error)).toBe(input.error.message) + }) + test("maps too_many_requests json messages", () => { const error = wrap(JSON.stringify({ type: "error", error: { type: "too_many_requests" } })) expect(SessionRetry.retryable(error)).toBe("Too Many Requests")