From 92648e2e12b55e1e832bfba0498376345d2d02f1 Mon Sep 17 00:00:00 2001 From: Oliver Chen <59662605+ochen1@users.noreply.github.com> Date: Fri, 2 Jan 2026 11:10:44 -0700 Subject: [PATCH] fix: handle missing file gracefully when processing file attachments When a user attaches a file that no longer exists, the stat() call would throw an unhandled ENOENT error that printed raw to the TUI, breaking the display. Now the error is caught and handled gracefully: - A Session.Event.Error is published (shows toast in TUI) - An error message is added to the conversation as a synthetic part - The message is still created with the user's intent preserved This follows the existing pattern used by ReadTool error handling. --- packages/opencode/src/session/prompt.ts | 29 ++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index b635cee7fb9..e363d11b292 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -827,7 +827,34 @@ 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(() => null) + + if (!stat) { + const message = `File not found: ${filepath}` + log.error("file not found", { filepath }) + Bus.publish(Session.Event.Error, { + sessionID: input.sessionID, + error: new NamedError.Unknown({ message }).toObject(), + }) + return [ + { + id: Identifier.ascending("part"), + messageID: info.id, + sessionID: input.sessionID, + type: "text", + synthetic: true, + text: message, + }, + { + ...part, + id: part.id ?? Identifier.ascending("part"), + messageID: info.id, + sessionID: input.sessionID, + }, + ] + } if (stat.isDirectory()) { part.mime = "application/x-directory"