{"record":{"id":"f56862efe35fda11","repo":"sigoden/aichat","slug":"failed-to-read-json-stream-err","errorCode":null,"errorMessage":"Failed to read json stream, {err}","messagePattern":"Failed to read json stream, (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/client/stream.rs","lineNumber":154,"sourceCode":"                }\n                es.close();\n            }\n        }\n    }\n    Ok(())\n}\n\npub async fn json_stream<S, F, E>(mut stream: S, mut handle: F) -> Result<()>\nwhere\n    S: Stream<Item = Result<bytes::Bytes, E>> + Unpin,\n    F: FnMut(&str) -> Result<()>,\n    E: std::error::Error,\n{\n    let mut parser = JsonStreamParser::default();\n    let mut unparsed_bytes = vec![];\n    while let Some(chunk_bytes) = stream.next().await {\n        let chunk_bytes =\n            chunk_bytes.map_err(|err| anyhow!(\"Failed to read json stream, {err}\"))?;\n        unparsed_bytes.extend(chunk_bytes);\n        match std::str::from_utf8(&unparsed_bytes) {\n            Ok(text) => {\n                parser.process(text, &mut handle)?;\n                unparsed_bytes.clear();\n            }\n            Err(_) => {\n                continue;\n            }\n        }\n    }\n    if !unparsed_bytes.is_empty() {\n        let text = std::str::from_utf8(&unparsed_bytes)?;\n        parser.process(text, &mut handle)?;\n    }\n\n    Ok(())\n}","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/sigoden/aichat/blob/82976d349ad97ac9aae0655ad631dace5e2a6385/src/client/stream.rs#L136-L172","documentation":"In src/client/stream.rs `json_stream` iterates an async byte stream and wraps any per-chunk transport failure with `Failed to read json stream, {err}`. It indicates the HTTP/byte stream carrying the JSON (e.g. a Gemini streaming completion) broke mid-read, before the JSON could be parsed.","triggerScenarios":"`json_stream` receives `Err(e)` from `stream.next().await` while consuming a streaming response (e.g. `gemini_chat_completions_streaming`): connection reset, timeout, proxy drop, TLS termination mid-body.","commonSituations":"Long-running LLM streaming responses dropped by flaky networks or proxies; server-side aborts; VPN/mobile connection changes mid-stream.","solutions":["Retry the request; streaming drops are often transient.","Check network/proxy stability (timeouts on corporate proxies commonly kill SSE streams).","Reduce response size or use non-streaming mode if the network drops long streams.","Inspect the inner error `{err}` for the root transport cause (e.g. hyper `IncompleteMessage`)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Rust\nmatch json_stream(...).await {\n    Err(e) if e.to_string().contains(\"Failed to read json stream\") => {\n        // log inner cause, backoff, and retry the request\n        retry_with_backoff(|| gemini_chat_completions_streaming(...)).await?;\n    }\n    other => other?,\n}","preventionTips":["Use stable networks or wire connections for long streams.","Configure proxy/client timeouts larger than expected stream duration.","Prefer non-streaming mode on unreliable links.","Log the inner error source for diagnosis."],"tags":["streaming","network","http"],"backgroundTag":"network-request-failed","analyzedSha":"82976d349ad97ac9aae0655ad631dace5e2a6385","analyzedAt":"2026-09-09T18:33:06.139Z","contentChangedAt":"2026-09-09T18:33:06.139Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}