summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTim <[email protected]>2026-01-27 07:50:04 +1300
committerGitHub <[email protected]>2026-01-26 13:50:04 -0500
commit837037cd04d94841cfa470eb8639f2397bd3ba87 (patch)
treead46eaa92b014caab9e33791277ddb38bd18fe5b
parentb0f865eae5a79742560ddea472fa8ba25871aa3d (diff)
downloadopencode-837037cd04d94841cfa470eb8639f2397bd3ba87.tar.gz
opencode-837037cd04d94841cfa470eb8639f2397bd3ba87.zip
fix: ensure openai 404 errors are retried (#10590)
-rw-r--r--packages/opencode/src/session/message-v2.ts9
-rw-r--r--packages/opencode/test/session/retry.test.ts15
2 files changed, 23 insertions, 1 deletions
diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts
index 7c8689037..650fda6e9 100644
--- a/packages/opencode/src/session/message-v2.ts
+++ b/packages/opencode/src/session/message-v2.ts
@@ -656,6 +656,13 @@ export namespace MessageV2 {
return result
}
+ const isOpenAiErrorRetryable = (e: APICallError) => {
+ const status = e.statusCode
+ if (!status) return e.isRetryable
+ // openai sometimes returns 404 for models that are actually available
+ return status === 404 || e.isRetryable
+ }
+
export function fromError(e: unknown, ctx: { providerID: string }) {
switch (true) {
case e instanceof DOMException && e.name === "AbortError":
@@ -724,7 +731,7 @@ export namespace MessageV2 {
{
message,
statusCode: e.statusCode,
- isRetryable: e.isRetryable,
+ isRetryable: ctx.providerID.startsWith("openai") ? isOpenAiErrorRetryable(e) : e.isRetryable,
responseHeaders: e.responseHeaders,
responseBody: e.responseBody,
metadata,
diff --git a/packages/opencode/test/session/retry.test.ts b/packages/opencode/test/session/retry.test.ts
index b130e927e..10137ed98 100644
--- a/packages/opencode/test/session/retry.test.ts
+++ b/packages/opencode/test/session/retry.test.ts
@@ -1,4 +1,5 @@
import { describe, expect, test } from "bun:test"
+import { APICallError } from "ai"
import { SessionRetry } from "../../src/session/retry"
import { MessageV2 } from "../../src/session/message-v2"
@@ -128,4 +129,18 @@ describe("session.message-v2.fromError", () => {
expect(retryable).toBeDefined()
expect(retryable).toBe("Connection reset by server")
})
+
+ test("marks OpenAI 404 status codes as retryable", () => {
+ const error = new APICallError({
+ message: "boom",
+ url: "https://api.openai.com/v1/chat/completions",
+ requestBodyValues: {},
+ statusCode: 404,
+ responseHeaders: { "content-type": "application/json" },
+ responseBody: "{\"error\":\"boom\"}",
+ isRetryable: false,
+ })
+ const result = MessageV2.fromError(error, { providerID: "openai" }) as MessageV2.APIError
+ expect(result.data.isRetryable).toBe(true)
+ })
})