Hmbown/CodeWhale · error
Zen Responses stream event
Error message
Zen Responses stream event
What it means
Test-side `.expect("Zen Responses stream event")` panic. A single item yielded by the SSE stream was an `Err`, i.e. the request started but one streamed event failed to parse or the stream aborted mid-way. The mock only sends `data: [DONE]\n\n`, so the failure means the Responses SSE framing/terminator handling does not accept that payload.
Solutions
- Log the failing event's error (`event.expect(...)` -> print `{e:?}`) to see whether it is a parse error or a stream transport error.
- Verify the mock response keeps `Content-Type: text/event-stream` and the body ends with `data: [DONE]\n\n`.
- Check the SSE decoder treats `data: [DONE]` as stream termination, not an unknown event kind.
- If you changed the fixture body, make every emitted event valid per the Responses wire parser.
Example fix
// before
while let Some(event) = stream.next().await {
event.expect("Zen Responses stream event");
}
// after (diagnosis)
while let Some(event) = stream.next().await {
event.unwrap_or_else(|e| panic!("Zen Responses stream event: {e:?}"));
} Defensive patterns
Strategy: try-catch
Validate before calling
// Rust (validate the mock body is well-formed SSE before the client runs)
assert!(body.contains("data: [DONE]"), "fixture must terminate the SSE stream"); Try / catch
while let Some(event) = stream.next().await {
if let Err(e) = event {
panic!("stream event failed: {e:?}");
}
} Prevention
- Always end mocked Responses streams with `data: [DONE]` and trailing blank line.
- Keep `Content-Type: text/event-stream` on SSE fixtures.
- Update the SSE decoder and its fixtures together when changing event shapes.
- Log the raw SSE frame before parsing in failing tests.
When it happens
Trigger: Running `opencode_zen_responses_request_uses_responses_route_without_oauth_headers` when the SSE parser rejects `data: [DONE]` (changed [DONE] semantics), emits an unexpected non-data line, or the server closes the stream in a way the event decoder treats as an error instead of a clean end.
Common situations: Upgrading the SSE/Responses event decoder; changing the mocked stream body to a multi-event fixture with a malformed event; Content-Type header dropped so the body is not parsed as an event stream.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- Zen Responses request should start
- Absolute path should not warn
- Antigravity cloud-code is stream-only; blocking…
- Chat Completions stream closed before [DONE] or…
- child assignment
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/921952c88a692980.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/client.rs:8134
Mock::given(method("POST"))
.and(path("/v1/responses"))
.respond_with(
ResponseTemplate::new(200)
.insert_header("Content-Type", "text/event-stream")
.set_body_string("data: [DONE]\n\n"),
)
.expect(1)
.mount(&server)
.await;
let client = opencode_zen_client(&server, "gpt-5.5");
assert_eq!(client.wire_format, WireFormat::Responses);
let mut stream = client
.create_message_stream(minimal_zen_request("gpt-5.5"))
.await
.expect("Zen Responses request should start");
while let Some(event) = stream.next().await {
event.expect("Zen Responses stream event");
}
let requests = server.received_requests().await.expect("recorded request");
assert_eq!(requests.len(), 1);
assert_zen_bearer_without_codex_headers(&requests[0]);
let body: Value = serde_json::from_slice(&requests[0].body).expect("Responses JSON body");
assert_eq!(body.get("model").and_then(Value::as_str), Some("gpt-5.5"));
assert!(body.get("input").is_some(), "Responses body: {body}");
assert!(body.get("messages").is_none(), "Responses body: {body}");
}
#[tokio::test]
async fn opencode_zen_messages_request_shape_uses_api_key_anthropic_route() {
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/v1/messages"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
"id": "msg_zen",View on GitHub (pinned to 73e0f67d83)