From c0a5fa9df4a77d6ddc34fbe4dbb32404b3629c8f Mon Sep 17 00:00:00 2001 From: Samir Alibabic Date: Thu, 23 Jul 2026 18:02:54 +0200 Subject: [PATCH] fix(session): retry flattened overload errors --- packages/opencode/src/session/message-v2.ts | 6 ++- packages/opencode/src/session/retry.ts | 5 ++- packages/opencode/test/session/retry.test.ts | 40 ++++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index 1bea9f52c3ec..323d56525b4d 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -700,7 +700,6 @@ export function fromError( { cause: e }, ).toObject() case e instanceof Error: - return new NamedError.Unknown({ message: errorMessage(e) }, { cause: e }).toObject() default: try { const parsed = ProviderError.parseStreamError(e) @@ -726,7 +725,10 @@ export function fromError( ).toObject() } } catch {} - return new NamedError.Unknown({ message: JSON.stringify(e) }, { cause: e }).toObject() + return new NamedError.Unknown( + { message: e instanceof Error ? errorMessage(e) : JSON.stringify(e) }, + { cause: e }, + ).toObject() } } diff --git a/packages/opencode/src/session/retry.ts b/packages/opencode/src/session/retry.ts index 4139665bd2bd..bda6decebc17 100644 --- a/packages/opencode/src/session/retry.ts +++ b/packages/opencode/src/session/retry.ts @@ -122,14 +122,15 @@ export function retryable(error: Err, provider: string) { return { message: error.data.message.includes("Overloaded") ? "Provider is overloaded" : error.data.message } } - // Check for rate limit patterns in plain text error messages + // Check for retryable patterns in plain text error messages const msg = isRecord(error.data) ? error.data.message : undefined if (typeof msg === "string") { const lower = msg.toLowerCase() if ( lower.includes("rate increased too quickly") || lower.includes("rate limit") || - lower.includes("too many requests") + lower.includes("too many requests") || + lower.includes("our servers are currently overloaded") ) { return { message: msg } } diff --git a/packages/opencode/test/session/retry.test.ts b/packages/opencode/test/session/retry.test.ts index 30ac879a6a9d..68de19261ec7 100644 --- a/packages/opencode/test/session/retry.test.ts +++ b/packages/opencode/test/session/retry.test.ts @@ -437,4 +437,44 @@ describe("session.message-v2.fromError", () => { message: "An error occurred while processing your request.", }) }) + + test("converts overloaded stream errors wrapped in Error to retryable APIError", () => { + const error = new Error( + JSON.stringify({ + type: "error", + error: { + type: "service_unavailable_error", + code: "server_is_overloaded", + message: "The server is overloaded. Please try again later.", + }, + }), + ) + const result = MessageV2.fromError(error, { providerID: ProviderV2.ID.make("openai") }) + + expect(SessionV1.APIError.isInstance(result)).toBe(true) + if (!SessionV1.APIError.isInstance(result)) throw new Error("expected APIError") + expect(result.data.isRetryable).toBe(true) + expect(SessionRetry.retryable(result, retryProvider)).toEqual({ + message: "The server is overloaded. Please try again later.", + }) + }) + + test("preserves unknown Error messages as UnknownError", () => { + const result = MessageV2.fromError(new Error("Unknown error"), { providerID }) + + expect(result).toStrictEqual({ + name: "UnknownError", + data: { + message: "Unknown error", + }, + }) + }) + + test("retries flattened OpenAI-compatible overload Errors", () => { + const error = new Error("Our servers are currently overloaded. Please try again later.") + const result = MessageV2.fromError(error, { providerID: ProviderV2.ID.make("openai") }) + + expect(result.name).toBe("UnknownError") + expect(SessionRetry.retryable(result, retryProvider)).toEqual({ message: error.message }) + }) })