Hmbown/CodeWhale · error

invalid persisted user image content kind

Error message

invalid persisted user image content kind

What it means

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.

Solutions

  1. Remove or convert the unsupported content block so only Text and ImageUrl blocks remain
  2. Regenerate/rewrite the persisted record with the current schema before validating
  3. If a new block kind must be allowed, update the matches! pattern in validate_stored_image_content explicitly

Example fix

// before: invalid block kind in stored image message
let blocks = vec![ContentBlock::Text{..}, ContentBlock::ToolUse{..}];
// after: only Text/ImageUrl blocks permitted
let blocks = vec![ContentBlock::Text{..}, ContentBlock::ImageUrl{..}];
Defensive patterns

Strategy: validation

Validate before calling

fn only_text_or_image(blocks: &[ContentBlock]) -> bool {
    blocks.iter().all(|b| matches!(b, ContentBlock::Text{..} | ContentBlock::ImageUrl{..}))
}

Type guard

fn is_image_input_block(b: &ContentBlock) -> bool {
    matches!(b, ContentBlock::Text { .. } | ContentBlock::ImageUrl { .. })
}

Try / catch

match validate_stored_image_content(blocks) {
    Ok(()) => /* proceed */,
    Err(e) if e.to_string().contains("invalid persisted user image content kind") => /* rewrite or migrate the stored record */,
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

                mime: mime.to_string(),
                data_base64: data.to_string(),
            });
        }
    }
    prepare_stored_images(&images)?;
    Ok(images)
}

/// Validate new image-bearing durable records without rewriting their block order.
/// Legacy schema 2 history continues to use its original interpretation.
pub(crate) fn validate_stored_image_content(blocks: &[ContentBlock]) -> Result<()> {
    if blocks.iter().any(|block| {
        !matches!(
            block,
            ContentBlock::Text { .. } | ContentBlock::ImageUrl { .. }
        )
    }) {
        bail!("invalid persisted user image content kind");
    }
    if runtime_images_from_blocks(blocks)?.is_empty() {
        bail!("persisted image input must contain an image");
    }
    Ok(())
}

/// Why a file could not be attached as an image.
///
/// Every variant renders to a sentence naming the file and the reason. These
/// strings reach both the user (as a command error) and the model (as an
/// in-band notice), so they say what to do next rather than only what failed.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ImageAttachError {
    /// The file could not be read at all.
    Unreadable { path: String, reason: String },
    /// The file is zero bytes.
    Empty { path: String },

View on GitHub (pinned to 73e0f67d83)