summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAiden Cline <[email protected]>2026-02-08 20:55:41 -0600
committerGitHub <[email protected]>2026-02-08 20:55:41 -0600
commit62f38087b8a1fa5eeebc14c376f0ebfb1d7b3e5c (patch)
tree880d38ad5aca8efc24f2ff258552e3f5a9911929
parent79879b43cee88d0701448725acc3d0cc1d7664d8 (diff)
downloadopencode-62f38087b8a1fa5eeebc14c376f0ebfb1d7b3e5c.tar.gz
opencode-62f38087b8a1fa5eeebc14c376f0ebfb1d7b3e5c.zip
fix: parse mid stream openai responses style errors to prevent infinite retries for errors that should STOP execution (#12768)
-rw-r--r--packages/opencode/src/session/message-v2.ts71
-rw-r--r--packages/opencode/test/session/message-v2.test.ts54
2 files changed, 124 insertions, 1 deletions
diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts
index 65ac72e05..3119c2bce 100644
--- a/packages/opencode/src/session/message-v2.ts
+++ b/packages/opencode/src/session/message-v2.ts
@@ -796,7 +796,76 @@ export namespace MessageV2 {
case e instanceof Error:
return new NamedError.Unknown({ message: e.toString() }, { cause: e }).toObject()
default:
- return new NamedError.Unknown({ message: JSON.stringify(e) }, { cause: e })
+ try {
+ const json = iife(() => {
+ if (typeof e === "string") {
+ try {
+ return JSON.parse(e)
+ } catch {
+ return undefined
+ }
+ }
+
+ if (typeof e === "object" && e !== null) {
+ return e
+ }
+ return undefined
+ })
+ if (json) {
+ const responseBody = JSON.stringify(json)
+ // Handle Responses API mid stream style errors
+ if (json?.type === "error") {
+ switch (json?.error?.code) {
+ case "context_length_exceeded":
+ return new MessageV2.APIError(
+ {
+ message: "Input exceeds context window of this model",
+ isRetryable: false,
+ responseBody,
+ },
+ {
+ cause: e,
+ },
+ ).toObject()
+ case "insufficient_quota":
+ return new MessageV2.APIError(
+ {
+ message: "Quota exceeded. Check your plan and billing details.",
+ isRetryable: false,
+ responseBody,
+ },
+ {
+ cause: e,
+ },
+ ).toObject()
+ case "usage_not_included":
+ return new MessageV2.APIError(
+ {
+ message:
+ "To use Codex with your ChatGPT plan, upgrade to Plus: https://chatgpt.com/explore/plus.",
+ isRetryable: false,
+ responseBody,
+ },
+ {
+ cause: e,
+ },
+ ).toObject()
+ case "invalid_prompt":
+ return new MessageV2.APIError(
+ {
+ message: json?.error?.message || "Invalid prompt.",
+ isRetryable: false,
+ responseBody,
+ },
+ {
+ cause: e,
+ },
+ ).toObject()
+ }
+ }
+ }
+ } catch {}
+ return new NamedError.Unknown({ message: JSON.stringify(e) }, { cause: e }).toObject()
}
}
}
diff --git a/packages/opencode/test/session/message-v2.test.ts b/packages/opencode/test/session/message-v2.test.ts
index 2f632ad1c..39c58bb6e 100644
--- a/packages/opencode/test/session/message-v2.test.ts
+++ b/packages/opencode/test/session/message-v2.test.ts
@@ -784,3 +784,57 @@ describe("session.message-v2.toModelMessage", () => {
])
})
})
+
+describe("session.message-v2.fromError", () => {
+ test("serializes response error codes", () => {
+ const cases = [
+ {
+ code: "context_length_exceeded",
+ message: "Input exceeds context window of this model",
+ },
+ {
+ code: "insufficient_quota",
+ message: "Quota exceeded. Check your plan and billing details.",
+ },
+ {
+ code: "usage_not_included",
+ message: "To use Codex with your ChatGPT plan, upgrade to Plus: https://chatgpt.com/explore/plus.",
+ },
+ {
+ code: "invalid_prompt",
+ message: "Invalid prompt from test",
+ },
+ ]
+
+ cases.forEach((item) => {
+ const input = {
+ type: "error",
+ error: {
+ code: item.code,
+ message: item.code === "invalid_prompt" ? item.message : undefined,
+ },
+ }
+ const result = MessageV2.fromError(input, { providerID: "test" })
+
+ expect(result).toStrictEqual({
+ name: "APIError",
+ data: {
+ message: item.message,
+ isRetryable: false,
+ responseBody: JSON.stringify(input),
+ },
+ })
+ })
+ })
+
+ test("serializes unknown inputs", () => {
+ const result = MessageV2.fromError(123, { providerID: "test" })
+
+ expect(result).toStrictEqual({
+ name: "UnknownError",
+ data: {
+ message: "123",
+ },
+ })
+ })
+})