headroomlabs-ai/headroom · error · TranslateError::MissingEventType

bedrock_eventstream_missing_event_type

bedrock_eventstream_missing_event_type

Error message

Bedrock message missing required `:event-type` header

What it means

The translator received an EventStream message that lacks the required :event-type header. Every AWS eventstream application message must carry :message-type and (for 'event' messages) :event-type; a message without it violates the wire format — AWS itself would never emit one. The handler 5xx's rather than guessing, per the loud-error policy.

Source

Thrown at crates/headroom-proxy/src/bedrock/eventstream_to_sse.rs:115

    /// `: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
    // indicate a service-side fault ON the stream (vs an HTTP-level
    // error from the initial request).
    if matches!(message.message_type(), Some("exception")) {
        let preview = String::from_utf8_lossy(&message.payload[..message.payload.len().min(160)])

View on GitHub (pinned to 322425c43b)

Solutions

  1. If this fires in tests, add the ":event-type" header (e.g. chunk, message_start) to the constructed message.
  2. If it fires in production, log the full header set of the offending message — headers that are present but misnamed (typo'd or non-lowercase) are the usual cause.
  3. Verify the decoded message came from the Bedrock response stream and not some other eventstream-carried protocol; the translator only understands Bedrock's event vocabulary.
  4. Check for decoder regressions where headers_len from the prelude is ignored or truncated.

Example fix

// before (test fixture)
let msg = EventStreamMessage { headers: vec![], payload: chunk_payload };

// after
let msg = EventStreamMessage {
    headers: vec![(":event-type".into(), "chunk".into())],
    payload: chunk_payload,
};
Defensive patterns

Strategy: validation

Validate before calling

// Before translating, require the header
use std::collections::HashMap;
fn has_event_type(headers: &HashMap<String, String>) -> bool {
    headers.contains_key(":event-type")
}

Type guard

fn is_translatable(msg: &EventStreamMessage) -> bool {
    msg.headers().any(|(k, _)| k == ":event-type")
}

Try / catch

match translate_message(&msg, mode) {
    Err(TranslateError::MissingEventType) => {
        tracing::error!(?msg, "wire-format violation from upstream; failing loudly");
        Err(UpstreamProtocolError) // handler 5xx's per project rule
    }
    r => r,
}

Prevention

When it happens

Trigger: translate_message() called on a decoded EventStreamMessage whose headers map has no :event-type key; typically caused by a decoder bug that dropped headers, a hand-constructed message in tests missing the header, or a non-AWS eventstream source that follows the framing but not the Bedrock header contract.

Common situations: Unit tests building EventStreamMessage values by hand without the :event-type header; a fork/change to the decoder that loses headers on a re-parse path; feeding generic eventstream data (e.g. from another AWS service) into the Bedrock translator.

Related errors


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