Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.

Commit 9eefcd1

Browse files
authored
Provider fix, anthropic Errorhandling if empty image file is read (anomalyco#5521)
1 parent 7c11241 commit 9eefcd1

2 files changed

Lines changed: 117 additions & 0 deletions

File tree

packages/opencode/src/provider/transform.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,20 @@ export namespace ProviderTransform {
171171
const filtered = msg.content.map((part) => {
172172
if (part.type !== "file" && part.type !== "image") return part
173173

174+
// Check for empty base64 image data
175+
if (part.type === "image") {
176+
const imageStr = part.image.toString()
177+
if (imageStr.startsWith("data:")) {
178+
const match = imageStr.match(/^data:([^;]+);base64,(.*)$/)
179+
if (match && (!match[2] || match[2].length === 0)) {
180+
return {
181+
type: "text" as const,
182+
text: "ERROR: Image file is empty or corrupted. Please provide a valid image.",
183+
}
184+
}
185+
}
186+
}
187+
174188
const mime = part.type === "image" ? part.image.toString().split(";")[0].replace("data:", "") : part.mediaType
175189
const filename = part.type === "file" ? part.filename : undefined
176190
const modality = mimeToModality(mime)

packages/opencode/test/provider/transform.test.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,3 +262,106 @@ describe("ProviderTransform.message - DeepSeek reasoning content", () => {
262262
expect(result[0].providerOptions?.openaiCompatible?.reasoning_content).toBeUndefined()
263263
})
264264
})
265+
266+
describe("ProviderTransform.message - empty image handling", () => {
267+
const mockModel = {
268+
id: "anthropic/claude-3-5-sonnet",
269+
providerID: "anthropic",
270+
api: {
271+
id: "claude-3-5-sonnet-20241022",
272+
url: "https://api.anthropic.com",
273+
npm: "@ai-sdk/anthropic",
274+
},
275+
name: "Claude 3.5 Sonnet",
276+
capabilities: {
277+
temperature: true,
278+
reasoning: false,
279+
attachment: true,
280+
toolcall: true,
281+
input: { text: true, audio: false, image: true, video: false, pdf: true },
282+
output: { text: true, audio: false, image: false, video: false, pdf: false },
283+
interleaved: false,
284+
},
285+
cost: {
286+
input: 0.003,
287+
output: 0.015,
288+
cache: { read: 0.0003, write: 0.00375 },
289+
},
290+
limit: {
291+
context: 200000,
292+
output: 8192,
293+
},
294+
status: "active",
295+
options: {},
296+
headers: {},
297+
} as any
298+
299+
test("should replace empty base64 image with error text", () => {
300+
const msgs = [
301+
{
302+
role: "user",
303+
content: [
304+
{ type: "text", text: "What is in this image?" },
305+
{ type: "image", image: "data:image/png;base64," },
306+
],
307+
},
308+
] as any[]
309+
310+
const result = ProviderTransform.message(msgs, mockModel)
311+
312+
expect(result).toHaveLength(1)
313+
expect(result[0].content).toHaveLength(2)
314+
expect(result[0].content[0]).toEqual({ type: "text", text: "What is in this image?" })
315+
expect(result[0].content[1]).toEqual({
316+
type: "text",
317+
text: "ERROR: Image file is empty or corrupted. Please provide a valid image.",
318+
})
319+
})
320+
321+
test("should keep valid base64 images unchanged", () => {
322+
const validBase64 =
323+
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
324+
const msgs = [
325+
{
326+
role: "user",
327+
content: [
328+
{ type: "text", text: "What is in this image?" },
329+
{ type: "image", image: `data:image/png;base64,${validBase64}` },
330+
],
331+
},
332+
] as any[]
333+
334+
const result = ProviderTransform.message(msgs, mockModel)
335+
336+
expect(result).toHaveLength(1)
337+
expect(result[0].content).toHaveLength(2)
338+
expect(result[0].content[0]).toEqual({ type: "text", text: "What is in this image?" })
339+
expect(result[0].content[1]).toEqual({ type: "image", image: `data:image/png;base64,${validBase64}` })
340+
})
341+
342+
test("should handle mixed valid and empty images", () => {
343+
const validBase64 =
344+
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
345+
const msgs = [
346+
{
347+
role: "user",
348+
content: [
349+
{ type: "text", text: "Compare these images" },
350+
{ type: "image", image: `data:image/png;base64,${validBase64}` },
351+
{ type: "image", image: "data:image/jpeg;base64," },
352+
],
353+
},
354+
] as any[]
355+
356+
const result = ProviderTransform.message(msgs, mockModel)
357+
358+
expect(result).toHaveLength(1)
359+
expect(result[0].content).toHaveLength(3)
360+
expect(result[0].content[0]).toEqual({ type: "text", text: "Compare these images" })
361+
expect(result[0].content[1]).toEqual({ type: "image", image: `data:image/png;base64,${validBase64}` })
362+
expect(result[0].content[2]).toEqual({
363+
type: "text",
364+
text: "ERROR: Image file is empty or corrupted. Please provide a valid image.",
365+
})
366+
})
367+
})

0 commit comments

Comments
 (0)