{"record":{"id":"eb98f4045a3256d8","repo":"Hmbown/CodeWhale","slug":"image-dimensions-exceed-the-decompression-bomb-guard","errorCode":null,"errorMessage":"image dimensions exceed the decompression bomb guard; downscale or crop first","messagePattern":"image dimensions exceed the decompression bomb guard; downscale or crop first","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/image_attach.rs","lineNumber":92,"sourceCode":"\npub(crate) fn decode_and_guard_image(bytes: &[u8]) -> Result<(DynamicImage, u32, u32)> {\n    let limits = || {\n        let mut limits = Limits::default();\n        limits.max_alloc = Some(MAX_DECODE_ALLOC_BYTES);\n        limits.max_image_width = Some(MAX_IMAGE_DIMENSION);\n        limits.max_image_height = Some(MAX_IMAGE_DIMENSION);\n        limits\n    };\n    let mut reader = ImageReader::new(Cursor::new(bytes)).with_guessed_format()?;\n    reader.limits(limits());\n    let (width, height) = reader\n        .into_dimensions()\n        .map_err(|_| anyhow::anyhow!(\"invalid image header or decompression bomb guard\"))?;\n    if u64::from(width) * u64::from(height) > MAX_IMAGE_PIXELS\n        || width > MAX_IMAGE_DIMENSION\n        || height > MAX_IMAGE_DIMENSION\n    {\n        bail!(\"image dimensions exceed the decompression bomb guard; downscale or crop first\");\n    }\n    let mut reader = ImageReader::new(Cursor::new(bytes)).with_guessed_format()?;\n    reader.limits(limits());\n    let decoded = reader\n        .decode()\n        .map_err(|_| anyhow::anyhow!(\"invalid image content or decode allocation limit\"))?;\n    Ok((decoded, width, height))\n}\n\n/// Validate untrusted inline input before route selection or durable admission.\n/// Return the existing provider-neutral history representation; no file is opened.\npub(crate) fn prepare_runtime_images(images: &[RuntimeImageInput]) -> Result<Vec<ContentBlock>> {\n    if images.len() > MAX_RUNTIME_IMAGES {\n        bail!(\"images exceed the {MAX_RUNTIME_IMAGES} attachment limit\");\n    }\n    prepare_images_with_limit(\n        images,\n        MAX_RUNTIME_IMAGE_BYTES,","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/image_attach.rs#L74-L110","documentation":"An image's declared pixel dimensions (width x height from the header, before full decode) exceed the decompression-bomb guard: the pixel count exceeds MAX_IMAGE_PIXELS or a single dimension exceeds MAX_IMAGE_DIMENSION. The library checks dimensions from the header first so hostile or accidental gigantic images are rejected before allocating decode memory.","triggerScenarios":"decode_and_guard_image is reached with bytes whose header advertises dimensions over the guard — e.g. a 20000x20000 PNG, a panorama, a scaled-up screenshot, or a crafted bomb image — via prepare_runtime_images, prepare_stored_images, prepare_tool_image_bytes, or process_media_file.","commonSituations":"Attaching a high-resolution camera panorama or scanned poster; a malicious upload designed to exhaust memory; a resized image saved with huge canvas dimensions but tiny content; scientific/medical images that legitimately exceed the dimension cap.","solutions":["Downscale or crop the image so both dimensions and total pixels are under the guard before attaching.","Re-encode as a reasonably sized JPEG/PNG (e.g. max side ~2048px) with an image tool or ImageMagick.","If legitimate large images are common, pre-process them at ingestion rather than at attach time.","For programmatic use, check width*height against MAX_IMAGE_PIXELS before calling the attach API."],"exampleFix":"// before\nmagick input.png attach.png            # keeps 18000x12000\n// after\nmagick input.png -resize 2048x2048\\> attach.png","handlingStrategy":"validation","validationCode":"fn header_dims_ok(bytes: &[u8]) -> bool {\n    ImageReader::new(Cursor::new(bytes))\n        .into_dimensions()\n        .map(|(w, h)| {\n            u64::from(w) * u64::from(h) <= MAX_IMAGE_PIXELS\n                && w <= MAX_IMAGE_DIMENSION && h <= MAX_IMAGE_DIMENSION\n        })\n        .unwrap_or(false)\n}","typeGuard":"fn guarded_dimensions(reader: &ImageReader<Cursor<&[u8]>>) -> Option<(u32, u32)> {\n    reader.into_dimensions().ok().filter(|(w, h)| {\n        u64::from(*w) * u64::from(*h) <= MAX_IMAGE_PIXELS\n    })\n}","tryCatchPattern":"match prepare_tool_image_bytes(&bytes) {\n    Err(e) if e.to_string().contains(\"decompression bomb guard\") => {\n        eprintln!(\"image too large; downscale to <= {} px per side\", MAX_IMAGE_DIMENSION); }\n    other => other?,\n}","preventionTips":["Check header dimensions before decode for any untrusted image","Downscale on ingestion (e.g. max side 2048) rather than at attach time","Never decode untrusted images without reader limits set"],"tags":["image","security","resource-limit"],"backgroundTag":"payload-too-large","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}