{"record":{"id":"cc451469f6e6e96a","repo":"BoundaryML/baml","slug":"unscheduledfuture-cannot-be-deserialized","errorCode":null,"errorMessage":"UnscheduledFuture cannot be deserialized","messagePattern":"UnscheduledFuture cannot be deserialized","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"baml_language/crates/bex_vm_types/src/types/future.rs","lineNumber":153,"sourceCode":"}\n\n// `UnscheduledFuture` is a runtime spawn-request slot — same lifecycle\n// shape as `Future`, never appears in a compiled `Program`. The pack\n// envelope (`baml_exec::PackEnvelope`) serializes the bytecode + the\n// constant heap; if an `UnscheduledFuture` ever reaches the serializer\n// that's a malformed program and we want to fail fast.\nimpl BorshSerialize for UnscheduledFuture {\n    fn serialize<W: std::io::Write>(&self, _writer: &mut W) -> std::io::Result<()> {\n        Err(std::io::Error::new(\n            std::io::ErrorKind::InvalidData,\n            \"UnscheduledFuture cannot be serialized\",\n        ))\n    }\n}\n\nimpl BorshDeserialize for UnscheduledFuture {\n    fn deserialize_reader<R: std::io::Read>(_reader: &mut R) -> std::io::Result<Self> {\n        Err(std::io::Error::new(\n            std::io::ErrorKind::InvalidData,\n            \"UnscheduledFuture cannot be deserialized\",\n        ))\n    }\n}\n\n// `Future::read` calls `MaybeUninit::<Value>::assume_init_read`, which is\n// sound only because `Value: Copy`. If `Value` ever gains a non-trivial\n// `Drop` (e.g. by holding an `Arc<…>` or `Box<…>`), `assume_init_read`\n// becomes UB on the second read. Guard against that at compile time.\nconst _: () = {\n    const fn assert_copy<T: Copy>() {}\n    assert_copy::<Value>();\n};\n\n/// Discriminant byte for [`Future::state`].\n#[repr(u8)]\nenum FutureTag {","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/baml_language/crates/bex_vm_types/src/types/future.rs#L135-L171","documentation":"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.","triggerScenarios":"Calling `UnscheduledFuture::deserialize_reader` / `borsh::from_reader::<UnscheduledFuture>`, or deserializing an `ObjectWire::UnscheduledFuture` payload during pack loading.","commonSituations":"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.","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."],"exampleFix":"// before: trusting a pack that embeds spawn slots\nlet obj: Object = borsh::from_reader(&mut reader)?; // io::Error: UnscheduledFuture cannot be deserialized\n// after: reject such artifacts up front / fix the pack source\nlet pack = PackEnvelope::parse(&bytes)?; // validates the object pool excludes runtime-only variants","handlingStrategy":"validation","validationCode":"// validate pack contents before deserializing objects\nif matches!(proxy, ObjectWire::UnscheduledFuture(_)) {\n    return Err(io::Error::new(io::ErrorKind::InvalidData, \"malformed pack: unscheduled future\"));\n}","typeGuard":"fn is_unscheduled_future_wire(o: &ObjectWire) -> bool {\n    matches!(o, ObjectWire::UnscheduledFuture(_))\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["rust","deserialization","borsh","fail-fast-invariant"],"backgroundTag":"unsupported-operation","analyzedSha":"bd85ce9dee1463ff04d27efd20531013a4ff46c1","analyzedAt":"2026-09-12T03:38:25.718Z","contentChangedAt":"2026-09-12T03:38:25.718Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}