{"record":{"id":"cf5dc9aeec3acefa","repo":"Hmbown/CodeWhale","slug":"sse-stream-request-failed-http-status-error-t","errorCode":null,"errorMessage":"SSE stream request failed: HTTP {status}: {error_text}","messagePattern":"SSE stream request failed: HTTP (.+?): (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/client/chat.rs","lineNumber":1395,"sourceCode":"\n        let (response, stream_idle_timeout) = self.open_chat_stream_response(&url, &body).await?;\n\n        let status = response.status();\n        crate::client::record_provider_response(self.api_provider, status.as_u16());\n        if !status.is_success() {\n            let raw_error_text = bounded_error_text(response, ERROR_BODY_MAX_BYTES).await;\n            let error_text = sanitize_http_error_body(\n                Some(self.api_provider.display_name()),\n                status.as_u16(),\n                &raw_error_text,\n            );\n            // If DeepSeek rejected for missing reasoning_content despite the\n            // sanitizer, dump the offending indices so we can diagnose where\n            // they came from on the next failure.\n            if error_text.contains(\"reasoning_content\") {\n                log_thinking_mode_violations(&body);\n            }\n            anyhow::bail!(\"SSE stream request failed: HTTP {status}: {error_text}\");\n        }\n\n        let api_provider = self.api_provider;\n        let base_url = self.base_url.clone();\n\n        // Capture transport-shape headers before we consume `response` into\n        // `bytes_stream()`. They are surfaced in the decode-error log path so\n        // we can tell HTTP/2 RST_STREAM from chunked-encoding corruption from\n        // gzip-compressor failure when investigating #103.\n        let response_headers = format_stream_headers(response.headers());\n        let byte_stream = response.bytes_stream();\n        let configured_reasoning_stream_style = self.reasoning_stream_style.clone();\n\n        let stream = async_stream::stream! {\n            use futures_util::StreamExt;\n\n            // Emit a synthetic MessageStart\n            yield Ok(StreamEvent::MessageStart {","sourceCodeStart":1377,"sourceCodeEnd":1413,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/client/chat.rs#L1377-L1413","documentation":"The streaming (SSE) Chat Completions request was rejected by the provider with a non-success status before any stream could open. The sanitized error body is re-raised with the HTTP status; additionally, if the provider's error mentions `reasoning_content`, the offending message indices are logged to diagnose thinking-mode replay violations. Raised in `handle_chat_completion_stream` in `crates/tui/src/client/chat.rs` around line 1395.","triggerScenarios":"Any non-2xx response when opening the SSE stream: 400 where the provider demands `reasoning_content` on assistant tool-call turns (DeepSeek thinking-mode replay violations), 401/403 auth, 429 rate limit, 5xx provider failure on stream open.","commonSituations":"DeepSeek thinking mode replaying assistant tool-call turns without `reasoning_content` despite the sanitizer; expired keys; bursts of autocomplete streams hitting rate limits; provider incidents breaking stream establishment.","solutions":["Read the sanitized `error_text` — for `reasoning_content` complaints, check the logged violation indices and ensure replayed assistant turns carry `reasoning_content`.","Retry with backoff for 429/5xx statuses.","Verify API key and provider model access for 401/403/404.","If thinking-mode replay keeps failing, disable thinking mode for the session or rebuild the offending turn's history."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"fn stream_open_precheck(api_key: &str, thinking_mode: bool, history: &[Value]) -> Result<(), String> {\n    if api_key.trim().is_empty() { return Err(\"missing API key\".into()); }\n    if thinking_mode && history.iter().any(is_assistant_tool_call_without_reasoning) {\n        return Err(\"history has assistant tool-call turns missing reasoning_content\".into());\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"match client.create_message_stream(req).await {\n    Err(e) if e.to_string().contains(\"SSE stream request failed\") => {\n        let s = extract_status(&e.to_string());\n        if s == 429 || s >= 500 { retry_with_backoff().await }\n        else if e.to_string().contains(\"reasoning_content\") { repair_reasoning_replay(); retry_once() }\n        else { Err(e) }\n    }\n    other => other,\n}","preventionTips":["Keep reasoning_content on replayed DeepSeek assistant tool-call turns.","Add backoff for stream-open 429/5xx; streams are bursty and rate-limit easily.","Inspect logged thinking-mode violation indices after reasoning_content failures.","Verify key/model access before opening streams."],"tags":["sse","streaming","http","api"],"backgroundTag":"http-error-response","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}