Hmbown/CodeWhale · error
invalid image header or decompression bomb guard
Error message
invalid image header or decompression bomb guard
What it means
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.
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.
Example fix
// before
let bytes = std::fs::read("screenshot.png.partial")?; // truncated
// after
let bytes = std::fs::read("screenshot.png")?; // complete file; header parses Defensive patterns
Strategy: validation
Validate before calling
let fmt = image::guess_format(&bytes)?; // fails early on non-image bytes let (w, h) = image::io::Reader::new(Cursor::new(&bytes)).with_guessed_format()?.into_dimensions()?; assert!(w > 0 && h > 0, "truncated or corrupt image header");
Try / catch
// Rust
match decode_and_guard_image(bytes, limits) {
Ok((img, w, h)) => attach(img),
Err(e) if e.to_string().contains("invalid image header") => {
eprintln!("attachment is not a readable image; re-export it");
}
Err(e) => return Err(e),
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- image dimensions exceed the decompression bomb guard…
- invalid image content or decode allocation limit
- agent action=claim widens an enforced write scope, and the…
- allowlisted read-only executable
- append_allow_rules only accepts action = "allow"
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/4d12f1a50e14f0ff.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/image_attach.rs:87
/// Maximum total pixels admitted before decoding is aborted (~33.5 megapixels).
pub const MAX_IMAGE_PIXELS: u64 = 33_554_432;
/// Memory allocation limit for image decoding (64 MiB).
pub const MAX_DECODE_ALLOC_BYTES: u64 = 64 * 1024 * 1024;
pub(crate) fn decode_and_guard_image(bytes: &[u8]) -> Result<(DynamicImage, u32, u32)> {
let limits = || {
let mut limits = Limits::default();
limits.max_alloc = Some(MAX_DECODE_ALLOC_BYTES);
limits.max_image_width = Some(MAX_IMAGE_DIMENSION);
limits.max_image_height = Some(MAX_IMAGE_DIMENSION);
limits
};
let mut reader = ImageReader::new(Cursor::new(bytes)).with_guessed_format()?;
reader.limits(limits());
let (width, height) = reader
.into_dimensions()
.map_err(|_| anyhow::anyhow!("invalid image header or decompression bomb guard"))?;
if u64::from(width) * u64::from(height) > MAX_IMAGE_PIXELS
|| width > MAX_IMAGE_DIMENSION
|| height > MAX_IMAGE_DIMENSION
{
bail!("image dimensions exceed the decompression bomb guard; downscale or crop first");
}
let mut reader = ImageReader::new(Cursor::new(bytes)).with_guessed_format()?;
reader.limits(limits());
let decoded = reader
.decode()
.map_err(|_| anyhow::anyhow!("invalid image content or decode allocation limit"))?;
Ok((decoded, width, height))
}
/// Validate untrusted inline input before route selection or durable admission.
/// Return the existing provider-neutral history representation; no file is opened.
pub(crate) fn prepare_runtime_images(images: &[RuntimeImageInput]) -> Result<Vec<ContentBlock>> {
if images.len() > MAX_RUNTIME_IMAGES {View on GitHub (pinned to 73e0f67d83)