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 to

View on GitHub (pinned to 322425c43b)

Solutions

  1. 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.
  2. For throttling: enable retry with backoff on the upstream call and reduce concurrent streaming requests against the account's TPS quota.
  3. For model/access errors: verify the model ID is available and invoked in that region and the IAM identity has bedrock:InvokeModelWithResponseStream.
  4. 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

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


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/ca9ac6663e155bc5. Report an issue: GitHub.