Hmbown/CodeWhale · error

persisted image input is missing its prompt

Error message

persisted image input is missing its prompt

What it means

When loading a persisted image-attachment turn input, the runtime requires a non-empty user prompt (text) to accompany the image content blocks. The store decoded the content successfully, but every text block was empty or whitespace-only, so the record is rejected. This guards against sessions that would otherwise send an image to the model with no instruction.

Solutions

  1. Re-open the message in the TUI and type a prompt before sending, then re-save/persist the record.
  2. Edit the persisted session record to add a non-empty text ContentBlock (or a non-empty `detail`) alongside the image blocks.
  3. Restore the session file from backup or start a new thread, re-attaching the image with a prompt.
  4. If caused by a version/migration path, update to a build whose attachment flow persists the prompt with the image.

Example fix

// before: persisted content has only an image block
[{"type":"image", "source":{...}}]

// after: include the prompt text block
[{"type":"text", "text":"Describe this screenshot"},{"type":"image", "source":{...}}]
Defensive patterns

Strategy: validation

Validate before calling

let has_prompt = blocks.iter().any(|b| matches!(b, ContentBlock::Text { text, .. } if !text.trim().is_empty()));
if !has_prompt {
    anyhow::bail!("attach a prompt before persisting image input");
}

Type guard

fn has_nonempty_text(blocks: &[ContentBlock]) -> bool {
    blocks.iter().any(|b| matches!(b, ContentBlock::Text { text, .. } if !text.trim().is_empty()))
}

Try / catch

match load_persisted_image_input(record) {
    Ok(blocks) => send(blocks),
    Err(e) if e.to_string().contains("missing its prompt") => prompt_user_for_text_then_resend(),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling the persisted-record loader (detail-to-content-blocks conversion in runtime_threads.rs) for a turn whose ContentBlock JSON parses but contains only image blocks, or whose `detail` field is empty/whitespace while no non-empty text block exists in the content.

Common situations: A session file was hand-edited or migrated and its text block was stripped; an older build crashed mid-save leaving image blocks without the prompt; a bug in the attachment flow wrote image content before capturing the detail text.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/src/runtime_threads.rs:1441

            || (self.schema_version == OUTPUT_LIMIT_RUNTIME_SCHEMA_VERSION
                && self
                    .metadata
                    .as_ref()
                    .is_some_and(|meta| meta.get("runtime_image_content").is_some())))
            && self.kind == TurnItemKind::UserMessage
        {
            let content = self
                .metadata
                .as_ref()
                .and_then(|meta| meta.get("runtime_image_content"))
                .context("persisted image input is missing its content")?;
            let blocks: Vec<ContentBlock> =
                serde_json::from_value(content.clone()).context("invalid persisted image input")?;
            crate::image_attach::validate_stored_image_content(&blocks)?;
            if self.detail.as_deref().is_some_and(|text| !text.trim().is_empty())
                && !blocks.iter().any(|block| matches!(block, ContentBlock::Text { text, .. } if !text.trim().is_empty()))
            {
                bail!("persisted image input is missing its prompt");
            }
            return Ok(blocks);
        }
        let text = self.detail.as_ref().unwrap_or(&self.summary);
        Ok(if text.trim().is_empty() {
            Vec::new()
        } else {
            vec![ContentBlock::Text {
                text: text.clone(),
                cache_control: None,
            }]
        })
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RuntimeEventRecord {
    #[serde(default = "default_runtime_schema_version")]

View on GitHub (pinned to 73e0f67d83)