[Bug]: Responses API silently drops malformed JSON chunks and hangs streams (Patch Ready)
Open 💬 3 comments Opened Jul 5, 2026 by DhruvTilva
### Description
There is a silent failure mode in the Responses API streaming handlers. When the backend streams a malformed or truncated JSON response chunk (e.g., due to a network blip or an unexpected upstream model output), the client silently swallows the error, drops the event, and continues the loop until the stream hangs and hits the `idle_timeout`.
### Root Cause Analysis
In both the SSE and WebSocket response stream loops, `serde_json::from_str` failures are caught, logged at the `debug!` level, and explicitly bypassed via a `continue` statement.
**SSE (`src/sse/responses.rs`):**
```rust
let event: ResponsesStreamEvent = match serde_json::from_str(&sse.data) {
Ok(event) => event,
Err(e) => {
debug!("Failed to parse SSE event: {e}, data: {}", &sse.data);
continue; // <-- Silently drops the malformed chunk
}
};
WebSocket (src/endpoint/responses_websocket.rs):
let event = match serde_json::from_str::<ResponsesStreamEvent>(&text) {
Ok(event) => event,
Err(err) => {
debug!("failed to parse websocket event: {err}, data: {text}");
continue; // <-- Silently drops the malformed chunk
}
};
Impact
- Silent Data Loss: Malformed
response.output_text.deltachunks are dropped, resulting in missing tokens in the client UI without any visible error. - Hanging Streams: If the terminal
response.completedevent is truncated or malformed, the loop never receives the completion signal. The stream hangs indefinitely until it hits theidle_timeout, yielding a misleading"stream closed before response.completed"error instead of surfacing the underlying parse failure.
Proposed Solution (Patch Ready)
We should enforce fail-fast behavior. When serde_json::from_str fails, the stream should immediately break and bubble up an ApiError::Stream containing the parse error.
I have already written and tested a patch that:
- Replaces the
continuestatements with proper error returns in bothresponses.rsandresponses_websocket.rs. - Adds an E2E test (
error_on_malformed_json_eventinsse_end_to_end.rs) that mocks a truncatedresponse.completedevent and asserts that the stream correctly halts and bubbles up the JSON parse error instead of hanging.
I have the branch pushed on my fork. I would be happy to submit the Pull Request if a maintainer is willing to invite/review it!
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗