summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMatt Silverlock <[email protected]>2026-02-18 12:45:27 -0500
committerGitHub <[email protected]>2026-02-18 11:45:27 -0600
commitd447b7694afc0080b78e7052b9de4c5a1a5f9eaf (patch)
tree7dad13c11d041762a4118f2835f3ccf2803b1fe9
parent5638b782c56e00bceeb029066811a0712c68e2ec (diff)
downloadopencode-d447b7694afc0080b78e7052b9de4c5a1a5f9eaf.tar.gz
opencode-d447b7694afc0080b78e7052b9de4c5a1a5f9eaf.zip
fix(github): emit PROMPT_TOO_LARGE error on context overflow (#14166)
-rw-r--r--packages/opencode/src/cli/cmd/github.ts39
-rw-r--r--packages/opencode/test/cli/github-action.test.ts38
2 files changed, 68 insertions, 9 deletions
diff --git a/packages/opencode/src/cli/cmd/github.ts b/packages/opencode/src/cli/cmd/github.ts
index c44b7f6a2..fd1a2f7e5 100644
--- a/packages/opencode/src/cli/cmd/github.ts
+++ b/packages/opencode/src/cli/cmd/github.ts
@@ -174,6 +174,18 @@ export function extractResponseText(parts: MessageV2.Part[]): string | null {
throw new Error("Failed to parse response: no parts returned")
}
+/**
+ * Formats a PROMPT_TOO_LARGE error message with details about files in the prompt.
+ * Content is base64 encoded, so we calculate original size by multiplying by 0.75.
+ */
+export function formatPromptTooLargeError(files: { filename: string; content: string }[]): string {
+ const fileDetails =
+ files.length > 0
+ ? `\n\nFiles in prompt:\n${files.map((f) => ` - ${f.filename} (${((f.content.length * 0.75) / 1024).toFixed(0)} KB)`).join("\n")}`
+ : ""
+ return `PROMPT_TOO_LARGE: The prompt exceeds the model's context limit.${fileDetails}`
+}
+
export const GithubCommand = cmd({
command: "github",
describe: "manage GitHub agent",
@@ -803,6 +815,7 @@ export const GithubRunCommand = cmd({
replacement,
})
}
+
return { userPrompt: prompt, promptFiles: imgData }
}
@@ -910,10 +923,15 @@ export const GithubRunCommand = cmd({
// result should always be assistant just satisfying type checker
if (result.info.role === "assistant" && result.info.error) {
- console.error("Agent error:", result.info.error)
- throw new Error(
- `${result.info.error.name}: ${"message" in result.info.error ? result.info.error.message : ""}`,
- )
+ const err = result.info.error
+ console.error("Agent error:", err)
+
+ if (err.name === "ContextOverflowError") {
+ throw new Error(formatPromptTooLargeError(files))
+ }
+
+ const errorMsg = err.data?.message || ""
+ throw new Error(`${err.name}: ${errorMsg}`)
}
const text = extractResponseText(result.parts)
@@ -939,10 +957,15 @@ export const GithubRunCommand = cmd({
})
if (summary.info.role === "assistant" && summary.info.error) {
- console.error("Summary agent error:", summary.info.error)
- throw new Error(
- `${summary.info.error.name}: ${"message" in summary.info.error ? summary.info.error.message : ""}`,
- )
+ const err = summary.info.error
+ console.error("Summary agent error:", err)
+
+ if (err.name === "ContextOverflowError") {
+ throw new Error(formatPromptTooLargeError(files))
+ }
+
+ const errorMsg = err.data?.message || ""
+ throw new Error(`${err.name}: ${errorMsg}`)
}
const summaryText = extractResponseText(summary.parts)
diff --git a/packages/opencode/test/cli/github-action.test.ts b/packages/opencode/test/cli/github-action.test.ts
index 773d9eb6a..cd64bb59e 100644
--- a/packages/opencode/test/cli/github-action.test.ts
+++ b/packages/opencode/test/cli/github-action.test.ts
@@ -1,5 +1,5 @@
import { test, expect, describe } from "bun:test"
-import { extractResponseText } from "../../src/cli/cmd/github"
+import { extractResponseText, formatPromptTooLargeError } from "../../src/cli/cmd/github"
import type { MessageV2 } from "../../src/session/message-v2"
// Helper to create minimal valid parts
@@ -159,3 +159,39 @@ describe("extractResponseText", () => {
expect(extractResponseText(parts)).toBe("Here's what I found")
})
})
+
+describe("formatPromptTooLargeError", () => {
+ test("formats error without files", () => {
+ const result = formatPromptTooLargeError([])
+ expect(result).toBe("PROMPT_TOO_LARGE: The prompt exceeds the model's context limit.")
+ })
+
+ test("formats error with files (base64 content)", () => {
+ // Base64 is ~33% larger than original, so we multiply by 0.75 to get original size
+ // 400 KB base64 = 300 KB original, 200 KB base64 = 150 KB original
+ const files = [
+ { filename: "screenshot.png", content: "a".repeat(400 * 1024) },
+ { filename: "diagram.png", content: "b".repeat(200 * 1024) },
+ ]
+ const result = formatPromptTooLargeError(files)
+
+ expect(result).toStartWith("PROMPT_TOO_LARGE: The prompt exceeds the model's context limit.")
+ expect(result).toInclude("Files in prompt:")
+ expect(result).toInclude("screenshot.png (300 KB)")
+ expect(result).toInclude("diagram.png (150 KB)")
+ })
+
+ test("lists all files when multiple present", () => {
+ // Base64 sizes: 4KB -> 3KB, 8KB -> 6KB, 12KB -> 9KB
+ const files = [
+ { filename: "img1.png", content: "x".repeat(4 * 1024) },
+ { filename: "img2.jpg", content: "y".repeat(8 * 1024) },
+ { filename: "img3.gif", content: "z".repeat(12 * 1024) },
+ ]
+ const result = formatPromptTooLargeError(files)
+
+ expect(result).toInclude("img1.png (3 KB)")
+ expect(result).toInclude("img2.jpg (6 KB)")
+ expect(result).toInclude("img3.gif (9 KB)")
+ })
+})