diff --git a/packages/opencode/src/provider/error.ts b/packages/opencode/src/provider/error.ts index 7318d8022..d83ea5000 100644 --- a/packages/opencode/src/provider/error.ts +++ b/packages/opencode/src/provider/error.ts @@ -145,14 +145,20 @@ export function parseStreamError(input: unknown): ParsedStreamError | undefined const responseBody = JSON.stringify(body) if (body.type !== "error") return + if ( + body?.error?.code === "server_error" || + body?.error?.code === "server_is_overloaded" || + body?.error?.type === "service_unavailable_error" + ) { + return { + type: "api_error", + message: typeof body?.error?.message === "string" ? body.error.message : "OpenAI server error", + isRetryable: true, + responseBody, + } + } + switch (body?.error?.code) { - case "server_error": - return { - type: "api_error", - message: typeof body?.error?.message === "string" ? body.error.message : "OpenAI server error", - isRetryable: true, - responseBody, - } case "context_length_exceeded": return { type: "context_overflow", diff --git a/packages/opencode/test/provider/error.test.ts b/packages/opencode/test/provider/error.test.ts index 7716453e9..b6937d086 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 service_unavailable_error events as retryable", () => { + const input = { + 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, + }, + } + + 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..775408dce 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 service_unavailable_error stream events", () => { + const input = { + 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, + }, + } + 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")