{"record":{"id":"900827775f8a10a3","repo":"Hmbown/CodeWhale","slug":"antigravity-cloud-code-stream-ended-without-a-text","errorCode":null,"errorMessage":"Antigravity cloud-code stream ended without a text part","messagePattern":"Antigravity cloud-code stream ended without a text part","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/client/cloud_code.rs","lineNumber":271,"sourceCode":"                        });\n                        yield Ok(StreamEvent::ContentBlockStart {\n                            index: 0,\n                            content_block: ContentBlockStart::Text {\n                                text: String::new(),\n                            },\n                        });\n                    }\n                    yield Ok(StreamEvent::ContentBlockDelta {\n                        index: 0,\n                        delta: Delta::TextDelta { text },\n                    });\n                }\n            }\n            if started {\n                yield Ok(StreamEvent::ContentBlockStop { index: 0 });\n                yield Ok(StreamEvent::MessageStop);\n            } else {\n                yield Err(anyhow::anyhow!(\n                    \"Antigravity cloud-code stream ended without a text part\"\n                ));\n            }\n        };\n        Ok(Box::pin(stream))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use crate::models::{Message, MessageRequest, SystemBlock, SystemPrompt};\n\n    fn text_request(model: &str, prompt: &str) -> MessageRequest {\n        MessageRequest {\n            model: model.to_string(),\n            messages: vec![Message {\n                role: \"user\".to_string(),","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/client/cloud_code.rs#L253-L289","documentation":"The cloud-code adapter emits MessageStart/ContentBlockDelta only after the first extractable text part; if the entire stream finishes with started == false - no frame ever yielded text - it errors with 'ended without a text part' instead of producing an empty assistant message. Non-text frames alone (usage, finish, envelope events) do not count as a text part.","triggerScenarios":"All model parts filtered server-side (safety filter, empty candidates); the response contains only tool-call or non-text parts this adapter cannot surface; the upstream returns envelope-only frames due to a request issue (bad model id, prompt) without an explicit error event.","commonSituations":"Safety-filtered or empty completions; model ids that return structured-only output; transient backend hiccups producing bare envelopes.","solutions":["Retry once - empty streams are often transient.","Replay with curl and inspect the frames: if they contain parts this adapter ignores, the response is not empty but an unsupported shape - report it.","Verify the model id and prompt on the cloud-code route actually produce text completions.","Check safety/blocking settings on the backend account."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Rust: retry once - empty completions are often transient\nErr(err) if err.to_string().contains(\"without a text part\") && attempt == 0 => {\n    attempt += 1;\n    continue;\n}\n","preventionTips":["Verify the model id produces text completions on the cloud-code route.","Check safety-filter settings if every turn comes back empty.","Inspect raw frames with curl before assuming a client bug."],"tags":["cloud-code","antigravity","empty-response","streaming","content-filter"],"backgroundTag":"empty-completion-response","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}