{"record":{"id":"566e069a161d4581","repo":"Hmbown/CodeWhale","slug":"stored-image-exceeds-the-attachment-limit","errorCode":null,"errorMessage":"stored image exceeds the attachment limit","messagePattern":"stored image exceeds the attachment limit","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/image_attach.rs","lineNumber":176,"sourceCode":"            decode_and_guard_image(&bytes)?;\n            // Standard padded base64 is the one replay representation.\n            if STANDARD.encode(&bytes) != image.data_base64 {\n                bail!(\"image {} base64 is not canonical\", index + 1);\n            }\n            Ok(attached.content_block())\n        })\n        .collect()\n}\n\n/// Reuse durable canonical bytes for retry, never reread a path or URL.\npub(crate) fn runtime_images_from_blocks(\n    blocks: &[ContentBlock],\n) -> Result<Vec<RuntimeImageInput>> {\n    let mut images = Vec::new();\n    for block in blocks {\n        if let ContentBlock::ImageUrl { image_url } = block {\n            if image_url.url.len() > MAX_IMAGE_BYTES.div_ceil(3) * 4 + 32 {\n                bail!(\"stored image exceeds the attachment limit\");\n            }\n            let (mime, data) = parse_data_url(&image_url.url)\n                .ok_or_else(|| anyhow::anyhow!(\"stored image requires canonical inline content\"))?;\n            images.push(RuntimeImageInput {\n                mime: mime.to_string(),\n                data_base64: data.to_string(),\n            });\n        }\n    }\n    prepare_stored_images(&images)?;\n    Ok(images)\n}\n\n/// Validate new image-bearing durable records without rewriting their block order.\n/// Legacy schema 2 history continues to use its original interpretation.\npub(crate) fn validate_stored_image_content(blocks: &[ContentBlock]) -> Result<()> {\n    if blocks.iter().any(|block| {\n        !matches!(","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/image_attach.rs#L158-L194","documentation":"This error is thrown by `runtime_images_from_blocks` in crates/tui/src/image_attach.rs when converting persisted ImageUrl content blocks into runtime image inputs. It guards the total stored payload size: the base64 data URL string must fit within MAX_IMAGE_BYTES (converted via div_ceil(3)*4 + 32 to account for base64 expansion). The library throws it to prevent oversized stored attachments from being re-loaded into a runtime request.","triggerScenarios":"Calling `runtime_images_from_blocks` (directly or via `validate_stored_image_content`) with a ContentBlock::ImageUrl whose image_url.url string length exceeds MAX_IMAGE_BYTES.div_ceil(3) * 4 + 32 characters.","commonSituations":"Resuming a session whose stored history contains an image attachment larger than the configured MAX_IMAGE_BYTES limit; a user hand-edited or migrated persisted session JSON with a huge base64 data URL; MAX_IMAGE_BYTES was lowered after the image was originally stored.","solutions":["Reduce the image size (resize/compress) before attaching so its base64 data URL fits within MAX_IMAGE_BYTES","Raise MAX_IMAGE_BYTES in image_attach.rs if the deployment genuinely needs larger attachments","Remove the oversized image block from the persisted session history before re-validating"],"exampleFix":"// before: huge image stored as data URL exceeds limit\nContentBlock::ImageUrl { image_url: ImageUrl { url: \"data:image/png;base64,<8MB-of-base64>\" } }\n// after: downscale/compress first so the data URL fits\nContentBlock::ImageUrl { image_url: ImageUrl { url: format!(\"data:{};base64,{}\", mime, small_base64) } }","handlingStrategy":"validation","validationCode":"const MAX_IMAGE_BYTES: usize = /* from image_attach.rs */;\nfn fits_limit(url: &str) -> bool {\n    url.len() <= MAX_IMAGE_BYTES.div_ceil(3) * 4 + 32\n}\n// check fits_limit(&image_url.url) before pushing the block","typeGuard":null,"tryCatchPattern":"match runtime_images_from_blocks(blocks) {\n    Ok(images) => /* use images */,\n    Err(e) if e.to_string().contains(\"exceeds the attachment limit\") => /* resize/compress or drop the image */,\n    Err(e) => return Err(e),\n}","preventionTips":["Compress/resize images before attaching so base64 size stays well under MAX_IMAGE_BYTES","Check the data URL length client-side before persisting a session","Keep MAX_IMAGE_BYTES stable or migrate stored records when changing it"],"tags":["image","attachment","size-limit","validation"],"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"}