{"record":{"id":"308c3d4c92092dcc","repo":"windmill-labs/windmill","slug":"image-file-is-too-large","errorCode":null,"errorMessage":"Image file is too large","messagePattern":"Image file is too large","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/imageUtils.ts","lineNumber":168,"sourceCode":"\tconst srcW = img.naturalWidth || img.width\n\tconst srcH = img.naturalHeight || img.height\n\tif (!srcW || !srcH) throw new Error('Image has no dimensions')\n\tif (srcW * srcH > MAX_IMAGE_PIXELS) throw new Error('Image resolution is too large')\n\tconst scale = Math.min(1, maxEdge / Math.max(srcW, srcH))\n\tconst w = Math.max(1, Math.round(srcW * scale))\n\tconst h = Math.max(1, Math.round(srcH * scale))\n\tconst canvas = document.createElement('canvas')\n\tcanvas.width = w\n\tcanvas.height = h\n\tconst ctx = canvas.getContext('2d')\n\tif (!ctx) throw new Error('Canvas 2D context unavailable')\n\tctx.drawImage(img, 0, 0, w, h)\n\treturn { ...encodeCanvas(canvas), name }\n}\n\n/** Read a user-provided image file and produce a bounded, model-ready AttachedImage. */\nexport async function fileToAttachedImage(file: File | Blob): Promise<AttachedImage> {\n\tif (file.size > MAX_IMAGE_BYTES) throw new Error('Image file is too large')\n\tconst name = file instanceof File ? file.name : undefined\n\tconst dataUrl = await blobToDataUrl(file)\n\treturn await normalizeImageDataUrl(dataUrl, name)\n}\n\n/** Split a data URL into its media type and base64 payload (for the Anthropic converter). */\nexport function parseImageDataUrl(url: string): { mediaType: string; base64: string } {\n\tconst match = /^data:([^;,]+)?(;base64)?,(.*)$/s.exec(url)\n\tif (!match) return { mediaType: 'image/png', base64: '' }\n\treturn { mediaType: match[1] || 'image/png', base64: match[2] ? match[3] : '' }\n}\n\n/** Build the OpenAI-format image content part that all three provider paths convert from. */\nexport function dataUrlToImagePart(dataUrl: string): ChatCompletionContentPartImage {\n\treturn { type: 'image_url', image_url: { url: dataUrl } }\n}\n\n/** Whether any message still carries an image_url content part. */","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/imageUtils.ts#L150-L186","documentation":"fileToAttachedImage() is the entry point for user-supplied image files and first enforces a byte-size budget: MAX_IMAGE_BYTES (defined in imageUtils.ts). Files larger than this are rejected before even being read into a data URL, preventing huge Blob reads and base64 inflation (base64 adds ~33%) from entering the pipeline. Unlike the pixel check (435), this fires on encoded file size before any decoding.","triggerScenarios":"Calling addImages/fileToAttachedImage with a File or Blob whose file.size > MAX_IMAGE_BYTES — e.g. a multi-megabyte screenshot PNG, a 10MB scanned PDF page export, or an unedited camera JPEG.","commonSituations":"Pasting large screenshots from Retina displays; dragging photos straight from a camera SD card; programmatic uploads of uncompressed PNGs (UI mockups exported as PNG can be tens of MB).","solutions":["Compress the image first (export as JPEG/WebP, reduce dimensions) so it fits under MAX_IMAGE_BYTES.","Check file.size in the picker before accepting the file and warn the user immediately.","Convert PNG screenshots to JPEG (photographic content) or reduce PNG bit depth.","If the limit is genuinely too low for your use case, raise MAX_IMAGE_BYTES in imageUtils.ts, noting downstream pixel limits still apply."],"exampleFix":"// before\nawait fileToAttachedImage(rawScreenshot) // 12 MB PNG\n// after\nif (rawScreenshot.size > MAX_IMAGE_BYTES) {\n  rawScreenshot = await compressToJpeg(rawScreenshot, { maxEdge: 2048, quality: 0.85 })\n}\nawait fileToAttachedImage(rawScreenshot)","handlingStrategy":"validation","validationCode":"const MAX_IMAGE_BYTES = 10 * 1024 * 1024 // check imageUtils.ts for the exact value\nif (file.size > MAX_IMAGE_BYTES) {\n  file = await compressToJpeg(file, { maxEdge: 2048, quality: 0.85 })\n}\nawait fileToAttachedImage(file)","typeGuard":null,"tryCatchPattern":"try {\n  const attached = await fileToAttachedImage(file)\n} catch (e) {\n  if (e instanceof Error && e.message === 'Image file is too large') {\n    const compressed = await compressToJpeg(file, { maxEdge: 2048, quality: 0.8 })\n    return await fileToAttachedImage(compressed)\n  } else throw e\n}","preventionTips":["Check file.size in the file input's accept/change handler and warn early","Auto-compress oversized files instead of rejecting them","Prefer JPEG/WebP exports for screenshots over raw PNG"],"tags":["image","validation","file-size"],"backgroundTag":"image-file-too-large","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}