From d447b7694afc0080b78e7052b9de4c5a1a5f9eaf Mon Sep 17 00:00:00 2001 From: Matt Silverlock Date: Wed, 18 Feb 2026 12:45:27 -0500 Subject: [PATCH] fix(github): emit PROMPT_TOO_LARGE error on context overflow (#14166) --- packages/opencode/src/cli/cmd/github.ts | 39 +++++++++++++++---- .../opencode/test/cli/github-action.test.ts | 38 +++++++++++++++++- 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)") + }) +})