Skip to content

Commit 4567d68

Browse files
committed
fix: handle missing temp files in file:// to data: URL conversion
Add error handling for clipboard image temp files that may be deleted before conversion. Uses async readFile in Promise.all path and try/catch with text fallback in both conversion locations.
1 parent 7518213 commit 4567d68

File tree

1 file changed

+34
-15
lines changed

1 file changed

+34
-15
lines changed

packages/opencode/src/session/message-v2.ts

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { Effect } from "effect"
1818
import { EffectLogger } from "@/effect/logger"
1919
import { fileURLToPath } from "url"
2020
import { readFileSync } from "fs"
21+
import { readFile } from "fs/promises"
2122

2223
/** Error shape thrown by Bun's fetch() when gzip/br decompression fails mid-stream */
2324
interface FetchDecompressionError extends Error {
@@ -631,21 +632,30 @@ export namespace MessageV2 {
631632
type: "content",
632633
value: [
633634
{ type: "text", text: obj.text },
634-
...attachments.map((attachment) => {
635-
if (attachment.url.startsWith("file://")) {
635+
...attachments
636+
.map((attachment) => {
637+
if (attachment.url.startsWith("file://")) {
638+
try {
639+
return {
640+
type: "media" as const,
641+
mediaType: attachment.mime,
642+
data: readFileSync(fileURLToPath(attachment.url)).toString("base64"),
643+
}
644+
} catch {
645+
return {
646+
type: "text" as const,
647+
text: `[Attached ${attachment.mime}: file not found]`,
648+
}
649+
}
650+
}
651+
const comma = attachment.url.indexOf(",")
636652
return {
637653
type: "media" as const,
638654
mediaType: attachment.mime,
639-
data: readFileSync(fileURLToPath(attachment.url)).toString("base64"),
655+
data: comma === -1 ? attachment.url : attachment.url.slice(comma + 1),
640656
}
641-
}
642-
const comma = attachment.url.indexOf(",")
643-
return {
644-
type: "media" as const,
645-
mediaType: attachment.mime,
646-
data: comma === -1 ? attachment.url : attachment.url.slice(comma + 1),
647-
}
648-
}),
657+
})
658+
.filter((p): p is { type: "media"; mediaType: string; data: string } => p !== null),
649659
],
650660
}
651661
}
@@ -840,11 +850,20 @@ export namespace MessageV2 {
840850
Promise.all(
841851
filtered.map((msg) =>
842852
Promise.all(
843-
msg.parts.map((part) => {
853+
msg.parts.map(async (part) => {
844854
if (part.type === "file" && part.url.startsWith("file://")) {
845-
return {
846-
...part,
847-
url: `data:${(part as any).mediaType};base64,${readFileSync(fileURLToPath(part.url)).toString("base64")}`,
855+
try {
856+
const buf = await readFile(fileURLToPath(part.url))
857+
return {
858+
...part,
859+
url: `data:${(part as any).mediaType};base64,${buf.toString("base64")}`,
860+
}
861+
} catch {
862+
return {
863+
...part,
864+
type: "text" as const,
865+
text: `[Attached ${(part as any).mediaType}: file not found]`,
866+
}
848867
}
849868
}
850869
return part

0 commit comments

Comments
 (0)