diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index ab1eba5be..3eec9ed4a 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -968,9 +968,11 @@ export namespace SessionPrompt { // have to normalize, symbol search returns absolute paths // Decode the pathname since URL constructor doesn't automatically decode it const filepath = fileURLToPath(part.url) - const stat = await Bun.file(filepath).stat() + const stat = await Bun.file(filepath) + .stat() + .catch(() => undefined) - if (stat.isDirectory()) { + if (stat?.isDirectory()) { part.mime = "application/x-directory" } @@ -989,7 +991,7 @@ export namespace SessionPrompt { // workspace/symbol searches, so we'll try to find the // symbol in the document to get the full range if (start === end) { - const symbols = await LSP.documentSymbol(filePathURI) + const symbols = await LSP.documentSymbol(filePathURI).catch(() => []) for (const symbol of symbols) { let range: LSP.Range | undefined if ("range" in symbol) { diff --git a/packages/opencode/test/session/prompt-missing-file.test.ts b/packages/opencode/test/session/prompt-missing-file.test.ts new file mode 100644 index 000000000..081847c67 --- /dev/null +++ b/packages/opencode/test/session/prompt-missing-file.test.ts @@ -0,0 +1,53 @@ +import path from "path" +import { describe, expect, test } from "bun:test" +import { Instance } from "../../src/project/instance" +import { Session } from "../../src/session" +import { SessionPrompt } from "../../src/session/prompt" +import { tmpdir } from "../fixture/fixture" + +describe("session.prompt missing file", () => { + test("does not fail the prompt when a file part is missing", async () => { + await using tmp = await tmpdir({ + git: true, + config: { + agent: { + build: { + model: "openai/gpt-5.2", + }, + }, + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const session = await Session.create({}) + + const missing = path.join(tmp.path, "does-not-exist.ts") + const msg = await SessionPrompt.prompt({ + sessionID: session.id, + agent: "build", + noReply: true, + parts: [ + { type: "text", text: "please review @does-not-exist.ts" }, + { + type: "file", + mime: "text/plain", + url: `file://${missing}`, + filename: "does-not-exist.ts", + }, + ], + }) + + if (msg.info.role !== "user") throw new Error("expected user message") + + const hasFailure = msg.parts.some( + (part) => part.type === "text" && part.synthetic && part.text.includes("Read tool failed to read"), + ) + expect(hasFailure).toBe(true) + + await Session.remove(session.id) + }, + }) + }) +})