BoundaryML/baml · error
UnscheduledFuture cannot be deserialized
Error message
UnscheduledFuture cannot be deserialized
What it means
Companion to the `UnscheduledFuture` serialization stub: its `BorshDeserialize` impl always returns `Err(InvalidData)` and can never succeed. A wire payload claiming to contain an `UnscheduledFuture` means a malformed program/pack, and the library fails fast instead of reconstructing a runtime spawn slot.
Source
Thrown at baml_language/crates/bex_vm_types/src/types/future.rs:153
}
// `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
// constant heap; if an `UnscheduledFuture` ever reaches the serializer
// that's a malformed program and we want to fail fast.
impl BorshSerialize for UnscheduledFuture {
fn serialize<W: std::io::Write>(&self, _writer: &mut W) -> std::io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"UnscheduledFuture cannot be serialized",
))
}
}
impl BorshDeserialize for UnscheduledFuture {
fn deserialize_reader<R: std::io::Read>(_reader: &mut R) -> std::io::Result<Self> {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"UnscheduledFuture cannot be deserialized",
))
}
}
// `Future::read` calls `MaybeUninit::<Value>::assume_init_read`, which is
// sound only because `Value: Copy`. If `Value` ever gains a non-trivial
// `Drop` (e.g. by holding an `Arc<…>` or `Box<…>`), `assume_init_read`
// becomes UB on the second read. Guard against that at compile time.
const _: () = {
const fn assert_copy<T: Copy>() {}
assert_copy::<Value>();
};
/// Discriminant byte for [`Future::state`].
#[repr(u8)]
enum FutureTag {View on GitHub (pinned to bd85ce9dee)
Solutions
- Regenerate the pack with the trusted compiler; an artifact containing this variant is malformed.
- Fix the producer so spawn-request values are excluded from the serialized constant heap.
- If persistence is needed, store a registry handle/id and re-create the spawn slot at runtime on load.
Example fix
// before: trusting a pack that embeds spawn slots let obj: Object = borsh::from_reader(&mut reader)?; // io::Error: UnscheduledFuture cannot be deserialized // after: reject such artifacts up front / fix the pack source let pack = PackEnvelope::parse(&bytes)?; // validates the object pool excludes runtime-only variants
Defensive patterns
Strategy: validation
Validate before calling
// validate pack contents before deserializing objects
if matches!(proxy, ObjectWire::UnscheduledFuture(_)) {
return Err(io::Error::new(io::ErrorKind::InvalidData, "malformed pack: unscheduled future"));
} Type guard
fn is_unscheduled_future_wire(o: &ObjectWire) -> bool {
matches!(o, ObjectWire::UnscheduledFuture(_))
} Prevention
- Only load packs produced by the trusted compiler; verify a format/version header before parsing objects.
- Treat `UnscheduledFuture` payloads in wire data as corruption — regenerate the artifact.
- Never serialize live runtime heaps; spawn slots must stay in the engine registry.
- Add a decoder-side check that rejects runtime-only variants with a domain-specific error message.
When it happens
Trigger: Calling `UnscheduledFuture::deserialize_reader` / `borsh::from_reader::<UnscheduledFuture>`, or deserializing an `ObjectWire::UnscheduledFuture` payload during pack loading.
Common situations: Loading a corrupted or hand-edited BAML pack whose object pool encodes an unscheduled-future object; a producer-side bug that serialized runtime spawn state (which itself would have failed first); format fuzzing.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Future cannot be deserialized
- UnscheduledFuture cannot be serialized
- invalid package interface: {message}
- {0}
- variant `{}` requires an explicit discriminant to stabilize
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/cc451469f6e6e96a.
Report an issue: GitHub.