Hmbown/CodeWhale · error
SSE stream request failed: HTTP {status}: {error_text}
Error message
SSE stream request failed: HTTP {status}: {error_text} What it means
The streaming (SSE) chat request was rejected with a non-2xx status before any body streamed. As a diagnostic special case, if the sanitized error text contains `reasoning_content`, the client logs `log_thinking_mode_violations` dumping the offending message indices — meaning the sanitizer failed to strip reasoning fields DeepSeek rejects for the selected model.
Source
Thrown at crates/tui/src/client/chat.rs:1230
let (response, stream_idle_timeout) = self.open_chat_stream_response(&url, &body).await?;
let status = response.status();
crate::client::record_provider_response(self.api_provider, status.as_u16());
if !status.is_success() {
let raw_error_text = bounded_error_text(response, ERROR_BODY_MAX_BYTES).await;
let error_text = sanitize_http_error_body(
Some(self.api_provider.display_name()),
status.as_u16(),
&raw_error_text,
);
// If DeepSeek rejected for missing reasoning_content despite the
// sanitizer, dump the offending indices so we can diagnose where
// they came from on the next failure.
if error_text.contains("reasoning_content") {
log_thinking_mode_violations(&body);
}
anyhow::bail!("SSE stream request failed: HTTP {status}: {error_text}");
}
let api_provider = self.api_provider;
let base_url = self.base_url.clone();
// Capture transport-shape headers before we consume `response` into
// `bytes_stream()`. They are surfaced in the decode-error log path so
// we can tell HTTP/2 RST_STREAM from chunked-encoding corruption from
// gzip-compressor failure when investigating #103.
let response_headers = format_stream_headers(response.headers());
let byte_stream = response.bytes_stream();
let configured_reasoning_stream_style = self.reasoning_stream_style.clone();
let stream = async_stream::stream! {
use futures_util::StreamExt;
// Emit a synthetic MessageStart
yield Ok(StreamEvent::MessageStart {View on GitHub (pinned to 8880682c63)
Solutions
- If the error text mentions `reasoning_content`, check the logged violation indices and strip reasoning blocks for the target model, or switch to the reasoner model
- Match thinking-mode settings to a model that supports them
- 401/429: fix credentials or back off, per the embedded status
- Verify the model ID and stream options against DeepSeek's current API
Defensive patterns
Strategy: retry
Type guard
fn is_retryable_http_status_from_message(msg: &str) -> bool {
[408, 409, 429, 500, 502, 503, 504]
.iter()
.any(|s| msg.contains(&format!("HTTP {s}")))
} Try / catch
match client.stream(request).await {
Err(e) if e.to_string().contains("reasoning_content") => {
// Model/thinking-mode mismatch: strip reasoning blocks or switch model, then retry
retry_without_reasoning_content()
}
Err(e) if is_retryable_http_status_from_message(&e.to_string()) => backoff_retry().await,
Err(e) => Err(e),
Ok(stream) => Ok(stream),
} Prevention
- Match thinking-mode parameters to models that support them; strip `reasoning_content` from history for chat models
- Read the logged violation indices when this fires — they name the offending message positions
- Verify stream-specific request options for the target model before enabling them
When it happens
Trigger: Sending `reasoning_content`-bearing messages or thinking-mode parameters to a non-reasoning DeepSeek model; 401/429/500-style rejections on the streaming endpoint; invalid `stream` parameters.
Common situations: Switching a session from DeepSeek reasoner to DeepSeek chat while history contains reasoning blocks; enabling thinking mode on a model that does not support it; auth or quota issues surfacing on the stream endpoint.
Related errors
- Runtime API request failed (${status}): ${message}
- FIM API error: HTTP {status}: {error_text}
- Stream read error: {e}
- SSE stream idle timeout after {}s — no data received (bytes_
- {err}
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/cf5dc9aeec3acefa.
Report an issue: GitHub.