headroomlabs-ai/headroom · error · TranslateError::UpstreamException
bedrock_upstream_exception
bedrock_upstream_exception
Error message
Bedrock stream emitted exception: {payload_preview} What it means
Raised by the eventstream-to-SSE translator when an inbound Bedrock EventStream message carries :message-type == exception — AWS's reserved value indicating the service raised a synchronous error mid-stream. The payload preview is embedded in the message. Per project rules this is surfaced loudly (mapped to a 5xx plus a log) rather than swallowed, because the stream is dead once an exception frame arrives.
Source
Thrown at crates/headroom-proxy/src/bedrock/eventstream_to_sse.rs:111
/// passthrough mode is handled by the streaming forwarder, which
/// never bothers parsing).
Emit(Bytes),
/// Skip — the message has no client-facing translation. Used for
/// `:event-type` values that AWS emits for protocol-internal
/// signalling (not yet observed for Bedrock Anthropic responses,
/// but we surface a structured outcome rather than guess).
Skip { event_type: String },
}
/// Errors during translation. Per project rules these are loud — the
/// handler 5xx's the client when an unknown `:message-type` arrives,
/// rather than silently swallowing.
#[derive(Debug, thiserror::Error)]
pub enum TranslateError {
/// `:message-type == exception`. AWS reserved value indicating the
/// service raised a synchronous error mid-stream. Surfaced as a
/// structured error so the handler can map it to a 5xx + log.
#[error("Bedrock stream emitted exception: {payload_preview}")]
UpstreamException { payload_preview: String },
/// The translator's input message is missing a required header
/// (`:event-type`). Wire-format violation — AWS would never emit.
#[error("Bedrock message missing required `:event-type` header")]
MissingEventType,
}
/// Translate one EventStream message under the chosen output mode.
///
/// Side-effect-free: emits a `tracing::info!` per `chunk` translation
/// and `tracing::warn!` for unknown event types. The hot path is
/// allocation-bounded — one `BytesMut` of `payload.len() + 8`.
pub fn translate_message(
message: &EventStreamMessage,
mode: OutputMode,
) -> Result<TranslateOutcome, TranslateError> {
// Always check for `:message-type == exception` first — that's a
// structural error regardless of mode. AWS reserves this value toView on GitHub (pinned to 322425c43b)
Solutions
- Read the payload_preview embedded in the error message — it contains the Bedrock exception JSON (e.g. throttlingException, modelStreamErrorException, accessDeniedException) which names the real cause.
- For throttling: enable retry with backoff on the upstream call and reduce concurrent streaming requests against the account's TPS quota.
- For model/access errors: verify the model ID is available and invoked in that region and the IAM identity has bedrock:InvokeModelWithResponseStream.
- For transient service errors: retry the whole request; do not attempt to resume a half-delivered SSE stream after an exception frame.
Defensive patterns
Strategy: retry
Try / catch
// Rust handler side
match translate_message(&msg, mode) {
Err(TranslateError::UpstreamException { payload_preview }) => {
tracing::error!(%payload_preview, "bedrock mid-stream exception");
// map to 5xx / terminate SSE with an error event; do not resume stream
abort_sse_with_error(payload_preview)
}
r => r,
} Prevention
- Log payload_preview verbatim — it contains the Bedrock exception name that drives the fix (throttle vs access vs model error).
- Retry whole requests with exponential backoff for throttling-class exceptions; never resume a partial stream.
- Keep per-account streaming concurrency under the Bedrock quota.
When it happens
Trigger: Calling Bedrock invoke-model-with-response-stream (or converse-stream) where the model/access path fails after the 200 headers were already sent: throttling mid-stream, model overloaded, content policy trip, or an internal service error emitted as an exception frame.
Common situations: Exceeding Bedrock quotas mid-generation; using a model ID not enabled in the region; IAM/invoked-but-revoked credentials that pass initial auth then fail on the stream; a very long generation crossing an idle/stream timeout.
Related errors
- bedrock_eventstream_missing_event_type
- bedrock_sigv4_failed
- upstream request failed: {0}
- invalid upstream URL: {0}
- {self.name} backend does not support OpenAI streaming
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/ca9ac6663e155bc5.
Report an issue: GitHub.