summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-04 10:20:54 -0600
committerAdam <[email protected]>2026-02-04 10:21:03 -0600
commit1721c6efdf4d6b4c786fb49aabc544e3b4a3b616 (patch)
treea3e6ecb90ae8bee68bd7af0235e7cf17455af557
parent93592702c33c5105b25f177c4db967a8db53cb69 (diff)
downloadopencode-1721c6efdf4d6b4c786fb49aabc544e3b4a3b616.tar.gz
opencode-1721c6efdf4d6b4c786fb49aabc544e3b4a3b616.zip
fix(core): session errors when attachment file not found
-rw-r--r--packages/opencode/src/session/prompt.ts8
-rw-r--r--packages/opencode/test/session/prompt-missing-file.test.ts53
2 files changed, 58 insertions, 3 deletions
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)
+ },
+ })
+ })
+})