{"record":{"id":"f5e6812a19ff39a2","repo":"Hmbown/CodeWhale","slug":"invalid-persisted-user-image-content-kind","errorCode":null,"errorMessage":"invalid persisted user image content kind","messagePattern":"invalid persisted user image content kind","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/image_attach.rs","lineNumber":199,"sourceCode":"                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!(\n            block,\n            ContentBlock::Text { .. } | ContentBlock::ImageUrl { .. }\n        )\n    }) {\n        bail!(\"invalid persisted user image content kind\");\n    }\n    if runtime_images_from_blocks(blocks)?.is_empty() {\n        bail!(\"persisted image input must contain an image\");\n    }\n    Ok(())\n}\n\n/// Why a file could not be attached as an image.\n///\n/// Every variant renders to a sentence naming the file and the reason. These\n/// strings reach both the user (as a command error) and the model (as an\n/// in-band notice), so they say what to do next rather than only what failed.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum ImageAttachError {\n    /// The file could not be read at all.\n    Unreadable { path: String, reason: String },\n    /// The file is zero bytes.\n    Empty { path: String },","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/image_attach.rs#L181-L217","documentation":"Thrown by `validate_stored_image_content` in crates/tui/src/image_attach.rs when a persisted user message intended to carry images contains a ContentBlock that is neither Text nor ImageUrl. Only these two block kinds are legal for stored image input; any other variant makes the persisted record invalid. This protects the reload path from deserializing unexpected block shapes into runtime requests.","triggerScenarios":"Calling `validate_stored_image_content` with a blocks slice containing any ContentBlock variant other than Text or ImageUrl (e.g. ToolUse, ToolResult, or a newer block type added after the record was written).","commonSituations":"Persisted session files written by a newer/older Codewhale version with different block kinds; hand-edited or migrated session JSON; a bug that stored tool blocks into a user image message.","solutions":["Remove or convert the unsupported content block so only Text and ImageUrl blocks remain","Regenerate/rewrite the persisted record with the current schema before validating","If a new block kind must be allowed, update the matches! pattern in validate_stored_image_content explicitly"],"exampleFix":"// before: invalid block kind in stored image message\nlet blocks = vec![ContentBlock::Text{..}, ContentBlock::ToolUse{..}];\n// after: only Text/ImageUrl blocks permitted\nlet blocks = vec![ContentBlock::Text{..}, ContentBlock::ImageUrl{..}];","handlingStrategy":"validation","validationCode":"fn only_text_or_image(blocks: &[ContentBlock]) -> bool {\n    blocks.iter().all(|b| matches!(b, ContentBlock::Text{..} | ContentBlock::ImageUrl{..}))\n}","typeGuard":"fn is_image_input_block(b: &ContentBlock) -> bool {\n    matches!(b, ContentBlock::Text { .. } | ContentBlock::ImageUrl { .. })\n}","tryCatchPattern":"match validate_stored_image_content(blocks) {\n    Ok(()) => /* proceed */,\n    Err(e) if e.to_string().contains(\"invalid persisted user image content kind\") => /* rewrite or migrate the stored record */,\n    Err(e) => return Err(e),\n}","preventionTips":["Only persist Text and ImageUrl blocks into user image messages","Version persisted session records and migrate on schema changes","Never hand-edit session JSON without validating block kinds afterwards"],"tags":["validation","schema","persistence","image"],"backgroundTag":"schema-validation-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}