BoundaryML/baml · error · io::Error

.bamlvalue header boundary id must be 16 bytes, got {}

Error message

.bamlvalue header boundary id must be 16 bytes, got {}

What it means

boundary_id_from_header converts the 16-byte boundary ID stored in a .bamlvalue file header into a [u8; 16] BoundaryId; if the header field is any other length, InvalidData is returned with the actual byte count. This guards against corrupt, truncated, or hand-written segment headers.

Source

Thrown at baml_language/crates/bex_events/src/history/mod.rs:638

            ),
        ));
    }
    fallback_dir
        .and_then(boundary_id_from_dir_name)
        .ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::InvalidData,
                format!(
                    "history boundary for project {} omitted canonical boundary id",
                    started.request.project_id.0
                ),
            )
        })
}

fn boundary_id_from_header(bytes: &[u8]) -> io::Result<BoundaryId> {
    let bytes: [u8; 16] = bytes.try_into().map_err(|_| {
        io::Error::new(
            io::ErrorKind::InvalidData,
            format!(
                ".bamlvalue header boundary id must be 16 bytes, got {}",
                bytes.len()
            ),
        )
    })?;
    Ok(BoundaryId::from_bytes(bytes))
}

fn boundary_id_from_dir_name(dir: &Path) -> Option<BoundaryId> {
    let name = dir.file_name()?.to_str()?;
    name.char_indices()
        .rev()
        .find_map(|(index, _)| BoundaryId::from_wire_str(&name[index..]))
}

fn capture_loss_replay_diagnostic(record: CaptureLossRecord) -> RunDiagnostic {

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Regenerate the .bamlvalue segments with the current library version so headers are well-formed.
  2. Check whether the file was truncated (compare size with the writer's expectation) and re-record if so.
  3. Ensure all producers and consumers of history files use the same bex_events version.
  4. Don't hand-edit segment files; craft test segments via the library's writer APIs.

Example fix

// before
let id = boundary_id_from_header(header_bytes)?;
// after
if header_bytes.len() != 16 {
    eprintln!("corrupt .bamlvalue header ({} bytes), re-recording", header_bytes.len());
    return re_record();
}
let id = boundary_id_from_header(header_bytes)?;
Defensive patterns

Strategy: validation

Validate before calling

if header_bytes.len() != 16 {
    return Err(format!(
        ".bamlvalue header boundary id is {} bytes, expected 16",
        header_bytes.len()
    ));
}

Type guard

fn is_valid_header(bytes: &[u8]) -> bool {
    bytes.len() >= 16
}

Try / catch

match boundary_id_from_header(header_bytes) {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidData => {
        Err(ArtifactError::CorruptHeader)
    }
    other => other,
}

Prevention

When it happens

Trigger: open_boundary_from_segments reading a .bamlvalue file whose header boundary-id field is not exactly 16 bytes — produced by an older/newer writer format, truncation, or manual file editing.

Common situations: Mixed bex_events versions writing incompatible header layouts; partially written files after a crash; tests crafting malformed byte segments directly.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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