diff --git a/packages/opencode/src/session/retry.ts b/packages/opencode/src/session/retry.ts index ec1116da0b..16fec29f3f 100644 --- a/packages/opencode/src/session/retry.ts +++ b/packages/opencode/src/session/retry.ts @@ -58,6 +58,19 @@ export namespace SessionRetry { return error.data.message.includes("Overloaded") ? "Provider is overloaded" : error.data.message } + // Check for rate limit patterns in plain text error messages + const msg = error.data?.message + if (typeof msg === "string") { + const lower = msg.toLowerCase() + if ( + lower.includes("rate increased too quickly") || + lower.includes("rate limit") || + lower.includes("too many requests") + ) { + return msg + } + } + const json = iife(() => { try { if (typeof error.data?.message === "string") { diff --git a/packages/opencode/test/session/retry.test.ts b/packages/opencode/test/session/retry.test.ts index dfeb7e9a40..0204c7a117 100644 --- a/packages/opencode/test/session/retry.test.ts +++ b/packages/opencode/test/session/retry.test.ts @@ -145,6 +145,25 @@ describe("session.retry.retryable", () => { expect(SessionRetry.retryable(error)).toBeUndefined() }) + test("retries plain text rate limit errors from Alibaba", () => { + const msg = + "Upstream error from Alibaba: Request rate increased too quickly. To ensure system stability, please adjust your client logic to scale requests more smoothly over time." + const error = wrap(msg) + expect(SessionRetry.retryable(error)).toBe(msg) + }) + + test("retries plain text rate limit errors", () => { + const msg = "Rate limit exceeded, please try again later" + const error = wrap(msg) + expect(SessionRetry.retryable(error)).toBe(msg) + }) + + test("retries too many requests in plain text", () => { + const msg = "Too many requests, please slow down" + const error = wrap(msg) + expect(SessionRetry.retryable(error)).toBe(msg) + }) + test("does not retry context overflow errors", () => { const error = new MessageV2.ContextOverflowError({ message: "Input exceeds context window of this model",