{"record":{"id":"1df69189947e4f2c","repo":"can1357/oh-my-pi","slug":"options-resolvedpath-is-not-a-decodable-optio","errorCode":null,"errorMessage":"${options.resolvedPath} is not a decodable ${options.image.mimeType} image: ${decodeFailure}","messagePattern":"(.+?) is not a decodable (.+?) image: (.+?)","errorType":"validation","errorClass":"InvalidImageDataError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/utils/image-loading.ts","lineNumber":300,"sourceCode":"\tresolvedPath: string;\n\ttextNotePrefix: string;\n\tautoResize: boolean;\n\tmaxBytes: number;\n\texcludeWebP: boolean | undefined;\n}\n\nasync function loadInMemoryImageInput(options: LoadInMemoryImageInputOptions): Promise<LoadedImageInput> {\n\tconst inputBytes = Buffer.byteLength(options.image.data, \"base64\");\n\tif (inputBytes > options.maxBytes) {\n\t\tthrow new ImageInputTooLargeError(inputBytes, options.maxBytes);\n\t}\n\n\t// Decode before anything else: a payload that cannot be decoded is rejected\n\t// by the provider for the whole request, so it must fail here — where the\n\t// caller still has a path to act on — instead of entering the transcript.\n\tconst decodeFailure = await imageDecodeFailureReason(options.image);\n\tif (decodeFailure !== null) {\n\t\tthrow new InvalidImageDataError(options.resolvedPath, options.image.mimeType, decodeFailure);\n\t}\n\n\tlet outputData = options.image.data;\n\tlet outputMimeType = options.image.mimeType;\n\tlet outputBytes = inputBytes;\n\tlet dimensionNote: string | undefined;\n\n\tconst shouldReencodeWebP = options.excludeWebP === true && options.image.mimeType === \"image/webp\";\n\tif (options.autoResize || shouldReencodeWebP) {\n\t\ttry {\n\t\t\tconst resized = await resizeImage(options.image, { excludeWebP: options.excludeWebP });\n\t\t\toutputData = resized.data;\n\t\t\toutputMimeType = resized.mimeType;\n\t\t\toutputBytes = resized.buffer.byteLength;\n\t\t\tdimensionNote = formatDimensionNote(resized);\n\t\t} catch {\n\t\t\t// Keep the original image when resize fails.\n\t\t}","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/utils/image-loading.ts#L282-L318","documentation":"loadInMemoryImageInput proactively decodes the image via imageDecodeFailureReason before it can enter the transcript, because an undecodable payload would be rejected by the LLM provider and fail the entire request. If decoding fails it throws InvalidImageDataError naming the path, mime type, and the decoder's reason.","triggerScenarios":"Calling loadInMemoryImageInput (via loadImageInput, loadSvgImageInput, or loadImageAttachmentInput) with bytes Bun.Image cannot decode: truncated files, corrupt downloads, zero-byte files, or files whose actual format mismatches the declared mimeType (e.g. a BMP renamed .png).","commonSituations":"Screenshot tool wrote a partial file; user attached a renamed or unsupported format; download interrupted; image is an exotic codec (HEIC, AVIF) the decoder lacks.","solutions":["Re-export or convert the file to PNG or JPEG and retry.","Verify the file opens in an image viewer; if not, re-download or re-capture it.","Fix the extension/mimeType so it matches the actual encoding.","Read the decoder's reason in the error message to distinguish truncation from unsupported format."],"exampleFix":"// before: await loadImageInput(\"/tmp/photo.heic\");  // after: convert to JPEG first (e.g. via sips/ffmpeg/sharp), then await loadImageInput(\"/tmp/photo.jpg\");","handlingStrategy":"validation","validationCode":"const failure = await imageDecodeFailureReason({ type: \"image\", data: base64, mimeType }); if (failure !== null) throw new Error(`cannot send image: ${failure}`);","typeGuard":"function looksPng(buf: Uint8Array): boolean { return buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47; } function looksJpeg(buf: Uint8Array): boolean { return buf[0] === 0xff && buf[1] === 0xd8; }","tryCatchPattern":"try { const img = await loadImageInput(path); } catch (err) { if (err instanceof InvalidImageDataError) { logger.warn(\"skipping undecodable image\", { path: path }); return null; } throw err; }","preventionTips":["Decode-probe images once at ingestion (the library's imageDecodeFailureReason is reusable).","Verify magic bytes match the declared extension/mimeType.","Reject zero-byte or truncated files (size below a minimal header size).","Convert exotic formats (HEIC, AVIF) to PNG/JPEG before attaching."],"tags":["image","decoding","validation"],"backgroundTag":"invalid-image-data","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}