{"record":{"id":"4d12f1a50e14f0ff","repo":"Hmbown/CodeWhale","slug":"invalid-image-header-or-decompression-bomb-guard","errorCode":null,"errorMessage":"invalid image header or decompression bomb guard","messagePattern":"invalid image header or decompression bomb guard","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/image_attach.rs","lineNumber":87,"sourceCode":"/// Maximum total pixels admitted before decoding is aborted (~33.5 megapixels).\npub const MAX_IMAGE_PIXELS: u64 = 33_554_432;\n\n/// Memory allocation limit for image decoding (64 MiB).\npub const MAX_DECODE_ALLOC_BYTES: u64 = 64 * 1024 * 1024;\n\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 {","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/image_attach.rs#L69-L105","documentation":"The image decoder could not read the image's dimensions, meaning the header is malformed or the format's declared size tripped the decoder's configured memory limits (the decompression-bomb guard applied via reader.limits()). This is a fail-fast check before the pixel-count guard runs.","triggerScenarios":"Calling decode_and_guard_image (via prepare_images_with_limit, prepare_tool_image_bytes, valid_tool_image, process_media_file, or read_media_over_budget_failure_names_conversion_recipe) with bytes that ImageReader::with_guessed_format + into_dimensions cannot parse: truncated files, non-image data, or headers claiming dimensions beyond the configured limits.","commonSituations":"Attaching a corrupted or partially downloaded image; pasting base64 that decodes to HTML/text rather than image data; a legitimately huge image whose header allocations exceed the decoder limits; wrong file extension or mis-detected format.","solutions":["Verify the bytes are a complete, valid image; re-export or re-download the file.","Re-encode the image (e.g. `magick convert in.png out.png`) to produce a clean header.","Downscale or crop the image so its header-declared dimensions fit within the configured limits."],"exampleFix":"// before\nlet bytes = std::fs::read(\"screenshot.png.partial\")?; // truncated\n\n// after\nlet bytes = std::fs::read(\"screenshot.png\")?; // complete file; header parses","handlingStrategy":"validation","validationCode":"let fmt = image::guess_format(&bytes)?; // fails early on non-image bytes\nlet (w, h) = image::io::Reader::new(Cursor::new(&bytes)).with_guessed_format()?.into_dimensions()?;\nassert!(w > 0 && h > 0, \"truncated or corrupt image header\");","typeGuard":null,"tryCatchPattern":"// Rust\nmatch decode_and_guard_image(bytes, limits) {\n    Ok((img, w, h)) => attach(img),\n    Err(e) if e.to_string().contains(\"invalid image header\") => {\n        eprintln!(\"attachment is not a readable image; re-export it\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Validate image bytes (magic-number/format check) before attaching.","Ensure downloads/uploads complete before decoding; check file sizes.","Keep source images modestly sized so headers never trip decoder limits."],"tags":["image","decoding","security"],"backgroundTag":"invalid-image-header","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"}