BoundaryML/baml · critical

Future cannot be serialized

Error message

Future cannot be serialized

What it means

Future's BorshSerialize impl is deliberately a hard failure: Futures are runtime-only values that never appear in a compiled Program. Serialization is rejected explicitly so a malformed program fails fast instead of silently encoding an unusable runtime handle.

Solutions

  1. Await the Future and serialize its resolved result instead of the Future itself.
  2. Exclude runtime-only values (futures, heap pointers) from anything you serialize; only compiled Program types are serializable.
  3. If this fires during normal program compilation/serialization, report it — a runtime-only Future leaked into the serialized Program, which is an invariant violation.
  4. Restructure your snapshotting to store only plain data (strings, numbers, completed values).

Example fix

// before: serializing a runtime value holding a Future
let blob = borsh::to_vec(&runtime_value_with_future)?;

// after: resolve first, then serialize the result
let resolved = vm.await_future(runtime_value_with_future)?;
let blob = borsh::to_vec(&resolved)?;
Defensive patterns

Strategy: type-guard

Type guard

// Rust: never serialize unresolved futures — check first
fn is_resolved(f: &FutureState) -> bool {
    matches!(f, FutureState::Ready(_))
}

Prevention

When it happens

Trigger: Serializing a VM value graph or structure that contains a Future (e.g. attempting to persist runtime state with pending async results, or a compiler/VM bug placing a Future into serialized program data).

Common situations: Caching serialized VM state that includes in-flight futures; trying to snapshot/serialize the raw runtime value instead of its resolved result; internal bugs routing runtime values into the compiled artifact path.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at baml_language/crates/bex_vm_types/src/types/future.rs:121

    /// `Ok(())` is "look at `state` for the actual outcome"; `Err(_)`
    /// carries an unrecoverable engine error for surfacing through the
    /// engine's `Await` resume path. It also carries weak Session eval leases
    /// that cancellation releases before waking the awaiter; keeping both in
    /// one Arc preserves `Future`'s interpreter-hot-loop size budget.
    settlement: Arc<FutureSettlement>,
}

// SAFETY: All access to `value` is gated by the Acquire/Release handshake
// on `state` and the single-writer invariant enforced by the
// `FutureManager`'s state mutex.
unsafe impl Send for Future {}
unsafe impl Sync for Future {}

// Futures are runtime-only; they never appear in a compiled Program. Reject
// serialization explicitly so a malformed program fails fast.
impl BorshSerialize for Future {
    fn serialize<W: std::io::Write>(&self, _writer: &mut W) -> std::io::Result<()> {
        Err(std::io::Error::new(
            std::io::ErrorKind::InvalidData,
            "Future cannot be serialized",
        ))
    }
}

impl BorshDeserialize for Future {
    fn deserialize_reader<R: std::io::Read>(_reader: &mut R) -> std::io::Result<Self> {
        Err(std::io::Error::new(
            std::io::ErrorKind::InvalidData,
            "Future cannot be deserialized",
        ))
    }
}

// `UnscheduledFuture` is a runtime spawn-request slot — same lifecycle
// shape as `Future`, never appears in a compiled `Program`. The pack
// envelope (`baml_exec::PackEnvelope`) serializes the bytecode + the

View on GitHub (pinned to bd85ce9dee)