{"record":{"id":"2269cab6a60cbd59","repo":"Hmbown/CodeWhale","slug":"antigravity-cloud-code-accepts-text-parts-only-no","errorCode":null,"errorMessage":"Antigravity cloud-code accepts text parts only; non-text content fails closed","messagePattern":"Antigravity cloud-code accepts text parts only; non-text content fails closed","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/client/cloud_code.rs","lineNumber":75,"sourceCode":"        bail!(\n            \"Antigravity cloud-code tools are not implemented yet; send a text-only turn or use the google provider\"\n        );\n    }\n    let mut contents = Vec::new();\n    for message in &request.messages {\n        let role = match message.role.as_str() {\n            \"user\" => \"user\",\n            \"assistant\" | \"model\" => \"model\",\n            other => bail!(\"Antigravity cloud-code does not accept role {other:?}\"),\n        };\n        let mut parts = Vec::new();\n        for block in &message.content {\n            match block {\n                ContentBlock::Text { text, .. } if !text.trim().is_empty() => {\n                    parts.push(json!({ \"text\": text }));\n                }\n                ContentBlock::Text { .. } => {}\n                _ => bail!(\n                    \"Antigravity cloud-code accepts text parts only; non-text content fails closed\"\n                ),\n            }\n        }\n        if !parts.is_empty() {\n            contents.push(json!({ \"role\": role, \"parts\": parts }));\n        }\n    }\n    if contents.is_empty() {\n        bail!(\"Antigravity cloud-code request has no text contents\");\n    }\n    let model = request.model.trim();\n    if model.is_empty() {\n        bail!(\"Antigravity cloud-code request is missing a model id\");\n    }\n    Ok(json!({\n        \"model\": model,\n        \"userAgent\": \"codewhale\",","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/client/cloud_code.rs#L57-L93","documentation":"The cloud-code builder only accepts text content blocks; any non-text block (image, tool_use, tool_result, etc.) triggers this bail. Empty or whitespace-only text blocks are silently skipped, but a non-text block fails closed with no guessing, because the cloud-code wire format only models text parts. This check sits inside the per-block loop, so it fires on the first offending block.","triggerScenarios":"Any message in the request containing a content block that is not `ContentBlock::Text` — attaching a screenshot, replaying a tool_use call, or including a tool_result block.","commonSituations":"Multimodal turns (images) sent to antigravity; replaying an assistant turn that contains tool calls from an earlier provider; tool outputs appended as structured blocks.","solutions":["Remove non-text blocks from the turn before sending on antigravity","Use the `google` provider for multimodal or tool-carrying turns","Flatten tool output into plain text if a text-only summary is acceptable"],"exampleFix":"// before\nmessage.content.push(ContentBlock::Image { .. }); // -> bail on antigravity\n\n// after\nlet blocks: Vec<_> = message.content.into_iter().filter(|b| matches!(b, ContentBlock::Text { .. })).collect();","handlingStrategy":"validation","validationCode":"for msg in &request.messages {\n    for block in &msg.content {\n        anyhow::ensure!(\n            matches!(block, ContentBlock::Text { .. }),\n            \"cloud-code accepts text parts only; found non-text block\"\n        );\n    }\n}","typeGuard":"fn content_is_text_only(messages: &[ChatMessage]) -> bool {\n    messages.iter().all(|m| m.content.iter().all(|b| matches!(b, ContentBlock::Text { .. })))\n}","tryCatchPattern":"match client.create_message_stream(request).await {\n    Ok(s) => Ok(s),\n    Err(err) if err.to_string().contains(\"text parts only\") => {\n        let text_only = strip_non_text_blocks(request); // degrade gracefully\n        client.create_message_stream(text_only).await\n    }\n    Err(err) => Err(err),\n}","preventionTips":["Check for images/tool blocks before switching a session to antigravity","Use the google provider for multimodal or tool-carrying turns","Render tool output as plain text when a text-only route is mandatory"],"tags":["antigravity","cloud-code","content-blocks","multimodal","rust"],"backgroundTag":"unsupported-content-type","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}