{"record":{"id":"49f2787c9ec26dc6","repo":"BoundaryML/baml","slug":"hostclosure-cannot-be-serialized","errorCode":null,"errorMessage":"HostClosure cannot be serialized","messagePattern":"HostClosure cannot be serialized","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"baml_language/crates/bex_vm_types/src/types/object.rs","lineNumber":263,"sourceCode":"                v.key_ty.clone(),\n                v.value_ty.clone(),\n                v.to_index_map()\n                    .into_iter()\n                    .map(|(k, v)| (k.to_string(), v))\n                    .collect(),\n            ),\n            Self::Float(v) => ObjectWire::Float(*v),\n            Self::Future(v) => ObjectWire::Future(v.clone()),\n            Self::UnscheduledFuture(v) => ObjectWire::UnscheduledFuture(v.clone()),\n            Self::Type(v) => ObjectWire::Type(Box::new(v.ty.clone())),\n            Self::RustData(_) => {\n                return Err(std::io::Error::new(\n                    std::io::ErrorKind::InvalidData,\n                    \"RustData cannot be serialized\",\n                ));\n            }\n            Self::HostClosure(_) => {\n                return Err(std::io::Error::new(\n                    std::io::ErrorKind::InvalidData,\n                    \"HostClosure cannot be serialized\",\n                ));\n            }\n            #[cfg(feature = \"heap_debug\")]\n            Self::Sentinel(_) => {\n                return Err(std::io::Error::new(\n                    std::io::ErrorKind::InvalidData,\n                    \"Sentinel cannot be serialized\",\n                ));\n            }\n        };\n        proxy.serialize(writer)\n    }\n}\n\nimpl BorshDeserialize for Object {\n    fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/baml_language/crates/bex_vm_types/src/types/object.rs#L245-L281","documentation":"`Object::HostClosure` is a closure backed by a host (Rust) callable that cannot be represented in the Borsh wire format, so `Object`'s `BorshSerialize` fails fast with `InvalidData` when it reaches that variant. Host closures are runtime-only values and must never end up in a serialized pack or heap snapshot.","triggerScenarios":"Serializing an `Object` (directly, or via `Value`/object-pool/pack export) holding `Self::HostClosure(_)` — typically a host-registered callback captured by VM code or stored in a reachable location at export time.","commonSituations":"Registering a Rust callback (e.g. for `f.cancel()` hooks or FFI bridges) that a BAML program stores in a global/map/array which is then included in pack serialization; heap-snapshot tooling run against a live session; accidental capture of host functions in compiled constants.","solutions":["Exclude host closures from the serialized value graph (drop or unregister them before exporting/packing).","Store host callables in a host-side registry and reference them from the VM by id (an `Object` variant that serializes as an integer).","Audit pack-export input: only serialize the compiled program's object pool, never the live runtime heap."],"exampleFix":"// before: exporting a heap where a map still holds a host callback\nlet pack = PackEnvelope::new(program, heap_containing_host_closure); // io::Error: HostClosure cannot be serialized\n// after: keep callbacks in a registry, reference by id\nlet id = host_registry.insert(my_callback);\nheap.store_global(\"on_event\", Value::Int(id as i64));\nlet pack = PackEnvelope::new(program, heap);","handlingStrategy":"validation","validationCode":"// before pack export, scan for host callables\nfn has_host_closure(v: &Value) -> bool {\n    matches!(v, Value::Object(o) if matches!(&*o.borrow(), Object::HostClosure(_)))\n}","typeGuard":"fn is_host_closure(o: &Object) -> bool {\n    matches!(o, Object::HostClosure(_))\n}","tryCatchPattern":null,"preventionTips":["Register host callbacks in a registry and reference them by id from VM code.","Unregister/drop host closures before any heap snapshot or pack export.","Never store host callables in globals, maps, or arrays that feed the serialized object pool.","Keep pack export operating on the compiled program only, not the runtime heap."],"tags":["rust","serialization","borsh","host-boundary"],"backgroundTag":"json-serialization-failed","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"}