BoundaryML/baml · error · io::Error

value record mixed run_started with run_completed lifecycle

Error message

value record mixed run_started with run_completed lifecycle metadata

What it means

When decoding a value file record (file_record_from_proto), a record carrying both run_started and run_completed lifecycle metadata is rejected: one record may describe at most one lifecycle event. This protects the event-file format's invariant that lifecycle bookkeeping is stored in dedicated records, and is surfaced as an io::Error with InvalidData kind from read_bamlvalue_from_bytes.

Source

Thrown at baml_language/crates/bex_events/src/value/read.rs:60

        records.push(file_record_from_proto(record)?);
        buf = rest;
    }

    Ok(BamlvalueContents {
        header,
        records,
        truncated,
    })
}

fn file_record_from_proto(record: pb::ValueRecordV1) -> io::Result<ValueFileRecord> {
    let has_run_started = record.run_started.is_some();
    let has_run_completed = record.run_completed.is_some();
    let has_lifecycle = has_run_started || has_run_completed;
    let has_log_event = record.log_event.is_some();
    let has_capture_loss = record.capture_loss.is_some();
    if has_run_started && has_run_completed {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            "value record mixed run_started with run_completed lifecycle metadata",
        ));
    }
    if has_lifecycle
        && (record.metadata.is_some()
            || !record.body.is_empty()
            || has_log_event
            || has_capture_loss
            || record.blob.is_some())
    {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            "value record mixed body metadata with lifecycle metadata",
        ));
    }
    if let Some(started) = record.run_started {
        return Ok(ValueFileRecord::RunStarted(started.try_into()?));

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Regenerate the value file by re-running the workflow; do not attempt to repair the mixed record by hand.
  2. If you author .bamlvalue files programmatically, emit run_started and run_completed as separate records.
  3. Check for concurrent writers — each run must write to its own value file.

Example fix

// before
record.run_started = Some(started);
record.run_completed = Some(completed); // same record
// after
write_record(ValueFileRecord::RunStarted(started));
write_record(ValueFileRecord::RunCompleted(completed));
Defensive patterns

Strategy: try-catch

Try / catch

match read_bamlvalue_from_bytes(&bytes) {
    Ok(v) => v,
    Err(e) if e.to_string().contains("lifecycle") => {
        eprintln!("value file corrupted (mixed lifecycle record): {e}");
        // fall back to re-running the workflow / restoring a backup
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: read_bamlvalue_from_bytes() on a value file whose protobuf record has both the run_started and run_completed oneofs set — typically a corrupted, hand-edited, or incorrectly merged file.

Common situations: Files corrupted by concurrent writers appending to the same value file, buggy external tooling that rewrites records, or merging/truncation mishaps that splice records together.

Related errors


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