BoundaryML/baml · error

capture loss omitted reason

Error message

capture loss omitted reason

What it means

CaptureLossV1.reason must be explicitly set (currently QueueFull) explaining why capture was lost; proto3's default Unspecified is rejected by the TryFrom conversion with InvalidData and this message. A loss record without a reason provides no actionable signal, so the reader treats it as malformed.

Source

Thrown at baml_language/crates/bex_events/src/value/record.rs:367

}

impl TryFrom<crate::value::pb::CaptureLossV1> for CaptureLossRecord {
    type Error = io::Error;

    fn try_from(value: crate::value::pb::CaptureLossV1) -> Result<Self, Self::Error> {
        let kind = match value.kind() {
            crate::value::pb::CaptureLossKind::Log => CaptureLossKind::Log,
            crate::value::pb::CaptureLossKind::Unspecified => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    "capture loss omitted kind",
                ));
            }
        };
        let reason = match value.reason() {
            crate::value::pb::CaptureLossReason::QueueFull => CaptureLossReason::QueueFull,
            crate::value::pb::CaptureLossReason::Unspecified => {
                return Err(io::Error::new(
                    io::ErrorKind::InvalidData,
                    "capture loss omitted reason",
                ));
            }
        };
        Ok(Self {
            kind,
            reason,
            skipped_count: value.skipped_count,
            call: value.call.map(TryInto::try_into).transpose()?,
            message: value.message,
            timestamp_ms: value.timestamp_ms,
        })
    }
}

impl From<&CaptureLossRecord> for crate::value::pb::CaptureLossV1 {
    fn from(value: &CaptureLossRecord) -> Self {

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Fix the producer to always set reason (e.g. CaptureLossReason::QueueFull) on loss records
  2. Set the enum explicitly when constructing the proto manually
  3. Apply a decode-time default (Unspecified -> QueueFull) if your contract only produces queue-full losses
  4. Align writer/reader versions and migrate legacy streams lacking the reason field

Example fix

// before
let loss = pb::CaptureLossV1 { kind: ..., ..Default::default() };
// after
let loss = pb::CaptureLossV1 { kind: ..., reason: pb::CaptureLossReason::QueueFull as i32, ..Default::default() };
Defensive patterns

Strategy: validation

Validate before calling

fn loss_reason_set(loss: &pb::CaptureLossV1) -> bool { loss.reason() != pb::CaptureLossReason::Unspecified }

Type guard

fn valid_loss_reason(loss: &pb::CaptureLossV1) -> bool { matches!(loss.reason(), pb::CaptureLossReason::QueueFull) }

Try / catch

CaptureLossRecord::try_from(loss).map_err(|e| if e.to_string().contains("omitted reason") { default_reason_record(loss) } else { e })

Prevention

When it happens

Trigger: Decoding a CaptureLossV1 whose reason field was left unset by the writer, or dropped when the message was rebuilt/copied by migration or proxy code.

Common situations: Older writers before the reason field existed, manually constructed loss records missing the enum setter, schema migrations defaulting new enum fields to Unspecified.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/2bce733f99aa9636. Report an issue: GitHub.