Hmbown/CodeWhale · error · ErrorEnvelope

provider stream error

Error message

provider stream error

What it means

When a mid-stream error frame arrives from the provider (an error event inside the stream payload), the engine surfaces it through the typed ErrorEnvelope contract, logs it, records it as the turn's stream error, and stops consuming. The message text is taken from the error frame's "message" field, defaulting to "provider stream error" when absent. Deltas after the failure frame are never forwarded because the stream is terminal.

Solutions

  1. Inspect the logged 'Provider stream error event: ...' warning for the provider's actual message to identify the cause.
  2. Retry the turn — mid-stream provider failures are frequently transient (overload, network blip).
  3. Check provider status page / API key validity and rate limits if it recurs.
  4. If the provider consistently errors mid-stream, reduce request complexity or update the provider adapter.
Defensive patterns

Strategy: retry

Try / catch

match turn_result { Err(e) if is_provider_stream_error(&e) => retry_with_backoff_limited(turn, 3), other => other }

Prevention

When it happens

Trigger: Provider sends an error event within an in-progress response stream (error JSON frame with/without a message field) at turn_loop.rs ~5226; the engine classifies it via ErrorEnvelope::classify and terminates consumption.

Common situations: Provider-side outages or overload mid-generation; invalid request rejected only after streaming began; rate limiting or auth token expiring mid-stream; provider emitting malformed error frames without a message.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/df21b128addeca7d. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/core/engine/turn_loop.rs:5226

                        usage_reported |= usage_has_reported_data(&u);
                        merge_stream_usage(&mut usage, u);
                    }
                }
                StreamEvent::MessageStop | StreamEvent::Ping => {}
                StreamEvent::Error { error } => {
                    // #3014: providers surface mid-stream failures as a
                    // chunk-level `error` object (chat.rs converts the frame
                    // to this event and keeps parsing later frames as
                    // deltas). Historically this arm only warned and kept
                    // consuming, so every delta after the failure frame —
                    // including reasoning — still rendered while the real
                    // error vanished into the retry tail. A mid-stream error
                    // frame is terminal for this stream: surface it through
                    // the same typed envelope contract, record it as the
                    // turn's stream error, and stop consuming. Deltas that
                    // arrive after the failure frame are never forwarded.
                    let message = error
                        .get("message")
                        .and_then(Value::as_str)
                        .unwrap_or("provider stream error");
                    let envelope = ErrorEnvelope::classify(message.to_string(), true);
                    crate::logging::warn(format!("Provider stream error event: {message}"));
                    let _ = self.tx_event.send(Event::error(envelope)).await;
                    stream_error.get_or_insert(message.to_string());
                    break;
                }
            }
        }
        // A stream cut at the provider's output limit ends without the
        // closing ContentBlockStop for whatever block was in flight. Those
        // blocks' inputs still hold the mid-stream mirror's best-effort
        // parse, which ignores `structure_synthesized` by design — left
        // as-is, a truncated tool call reaches dispatch through
        // `tool.input` and executes (#5986). Every block that never
        // stopped goes through the same finalization gate a normal
        // ContentBlockStop applies, and is announced with the same

View on GitHub (pinned to 433685b202)