[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

  1. Silent Data Loss: Malformed response.output_text.delta chunks are dropped, resulting in missing tokens in the client UI without any visible error.
  2. Hanging Streams: If the terminal response.completed event is truncated or malformed, the loop never receives the completion signal. The stream hangs indefinitely until it hits the idle_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:

  1. Replaces the continue statements with proper error returns in both responses.rs and responses_websocket.rs.
  2. Adds an E2E test (error_on_malformed_json_event in sse_end_to_end.rs) that mocks a truncated response.completed event 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!

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗