{"record":{"id":"2e3704b6f0408642","repo":"windmill-labs/windmill","slug":"image-has-no-dimensions","errorCode":null,"errorMessage":"Image has no dimensions","messagePattern":"Image has no dimensions","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/copilot/chat/imageUtils.ts","lineNumber":152,"sourceCode":"\treturn {\n\t\tdataUrl: (ctx ? flat : canvas).toDataURL('image/jpeg', 0.82),\n\t\tmediaType: 'image/jpeg'\n\t}\n}\n\n/**\n * Downscale a data URL to ≤ MAX_IMAGE_EDGE on its longest side and re-encode to\n * png/jpeg. Used by both the file-attach path and the screenshot tool.\n */\nexport async function normalizeImageDataUrl(\n\tdataUrl: string,\n\tname?: string,\n\tmaxEdge: number = MAX_IMAGE_EDGE\n): Promise<AttachedImage> {\n\tconst img = await loadImage(dataUrl)\n\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)","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/copilot/chat/imageUtils.ts#L134-L170","documentation":"normalizeImageDataUrl() loads a data URL into an HTMLImageElement and derives dimensions from naturalWidth/Height (falling back to width/height). If both are 0/falsy the image cannot be drawn or bounded, so the adapter throws this error immediately before any canvas work. It protects downstream resize/encode logic from division-by-zero and empty canvases.","triggerScenarios":"loadImage() resolved but img.naturalWidth/img.width or img.naturalHeight/img.height are 0 — e.g. the data URL decoded to an empty/broken image, an SVG without intrinsic dimensions, or a still-loading image passed directly.","commonSituations":"Attaching an SVG pasted from a design tool with no width/height attributes; a truncated base64 payload that still decodes; images constructed programmatically before load completed.","solutions":["Inspect the data URL: verify the base64 payload is complete and the MIME type matches the data.","For SVGs, add explicit width/height (or viewBox with fixed dimensions) before attaching.","Ensure loadImage() awaits the 'load' event, not just element creation, before normalizing.","Pre-convert odd formats (SVG/HEIC) to PNG/JPEG before attaching to the chat."],"exampleFix":"// before\nawait normalizeImageDataUrl(svgDataUrl) // SVG without dimensions\n// after\nsvgText = svgText.replace('<svg', '<svg width=\"1024\" height=\"768\"')\nconst fixed = 'data:image/svg+xml;base64,' + btoa(svgText)\nconst png = await rasterizeToPng(fixed)\nawait normalizeImageDataUrl(png)","handlingStrategy":"validation","validationCode":"async function hasDimensions(dataUrl: string): Promise<boolean> {\n  const img = new Image()\n  await new Promise((res, rej) => { img.onload = res; img.onerror = rej; img.src = dataUrl })\n  return (img.naturalWidth || img.width) > 0 && (img.naturalHeight || img.height) > 0\n}","typeGuard":"function isSizedImage(img: HTMLImageElement): boolean {\n  return (img.naturalWidth || img.width) > 0 && (img.naturalHeight || img.height) > 0\n}","tryCatchPattern":"try {\n  const attached = await fileToAttachedImage(file)\n} catch (e) {\n  if (e instanceof Error && e.message === 'Image has no dimensions') {\n    notify('This image has no readable dimensions; try PNG or JPEG')\n  } else throw e\n}","preventionTips":["Give SVGs explicit width/height before attaching","Pre-rasterize SVG/HEIC to PNG or JPEG","Verify base64 payloads are complete after copy/paste or truncation-prone transfers"],"tags":["image","browser","validation"],"backgroundTag":"image-no-dimensions","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"}