Hmbown/CodeWhale · error

invalid image content or decode allocation limit

Error message

invalid image content or decode allocation limit

What it means

After the dimension check passed, the actual decode of pixel data failed — the image content is corrupt or decoding would exceed the allocation limit set via reader.limits(). The dimension probe succeeding but the full decode failing usually means damaged compressed data (truncated IDAT/stream) rather than a bad header.

Solutions

  1. Re-export the image with a known-good encoder to repair the stream.
  2. Convert to a common format (PNG or JPEG) before attaching: `magick convert in.webp out.png`.
  3. Downscale the image so full decode fits within the allocation limit.

Example fix

// before
let img = image::load_from_memory(&truncated_bytes)?; // corrupt stream

// after
let img = image::load_from_memory(&re_encoded_png_bytes)?;
Defensive patterns

Strategy: validation

Validate before calling

// Pre-decode integrity probe
let probe = image::io::Reader::new(Cursor::new(&bytes)).with_guessed_format()?;
let dims = probe.into_dimensions()?;
let est = dims.0 as u64 * dims.1 as u64 * 4; // rough RGBA size
if est > decode_budget { bail!("image too large to decode; downscale first"); }

Try / catch

// Rust
match decode_and_guard_image(bytes, limits) {
    Ok(decoded) => use_image(decoded),
    Err(e) if e.to_string().contains("decode allocation") => {
        let fixed = re_encode_downscaled(&bytes)?;
        use_image(decode_and_guard_image(fixed, limits)?);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: decode_and_guard_image is called and reader.decode() fails on otherwise dimension-valid bytes: truncated JPEG/PNG streams, corrupt pixel data, or content whose decode buffer exceeds the configured memory limits.

Common situations: Interrupted uploads that pass a partial header check; images recompressed by buggy tools; animated or very large images whose full frame allocation exceeds the decode limit.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/2a91e14cb3c070f2. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/image_attach.rs:98

        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 {
        bail!("images exceed the {MAX_RUNTIME_IMAGES} attachment limit");
    }
    prepare_images_with_limit(
        images,
        MAX_RUNTIME_IMAGE_BYTES,
        Some(MAX_RUNTIME_IMAGE_TOTAL_BYTES),
    )
}

/// Internal Engine/history input retains the established local 5 MiB ceiling.
/// Network callers must first pass `prepare_runtime_images` (4 MiB per image,

View on GitHub (pinned to 73e0f67d83)