BoundaryML/baml · critical
HeapPtr cannot be deserialized: runtime pointer leaked into…
Error message
HeapPtr cannot be deserialized: runtime pointer leaked into serialized data
What it means
HeapPtr's BorshDeserialize impl is intentionally a hard failure: deserializing would have to fabricate a runtime heap pointer from stored bytes, which is never valid. Encountering this means runtime pointer data leaked into a serialized payload — a real bug that must fail fast rather than reconstruct a dangling pointer.
Solutions
- Regenerate the serialized Program from the compiler with a correct, HeapPtr-free build.
- Verify you are deserializing with the matching type/schema and library version used at serialization time.
- If the artifact is third-party or hand-edited, obtain it from a trusted source and re-check its integrity.
- Report it if normal compiled programs trigger this — it indicates a compiler bug leaking runtime pointers into serialized output.
Example fix
// before: decoding into a struct with a HeapPtr field
let snap: Snapshot = borsh::from_reader(&mut r)?; // Snapshot { obj: HeapPtr }
// after: decode into the HeapPtr-free Program object types
let prog: Program = borsh::from_reader(&mut r)?; Defensive patterns
Strategy: type-guard
Prevention
- Only deserialize Program artifacts produced by the same compiler/runtime version.
- Never decode arbitrary bytes into structs containing HeapPtr fields.
- Verify artifact integrity (checksums) when transferring serialized programs.
- Treat any hit as corruption/bug — deserialized data must never fabricate heap pointers.
When it happens
Trigger: Calling borsh deserialization on a byte blob whose structure contains a HeapPtr-typed field; loading a corrupted or hand-crafted serialized Program where a runtime pointer was encoded.
Common situations: Loading stale/foreign serialized artifacts produced by buggy tooling; deserializing into the wrong type so bytes are interpreted as a HeapPtr; tampering or corruption in shipped program files.
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
- HeapPtr is a runtime-only pointer and must not be serialized
- Future cannot be serialized
- type head carries a non-head tag
- UnscheduledFuture cannot be deserialized
- {0}
AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12).
Data as JSON: /api/errors/ac8a78656e45b0e0.
Report an issue: GitHub.
Appendix: source
Thrown at baml_language/crates/bex_vm_types/src/heap_ptr.rs:172
// envelope) — the addresses are meaningless outside the originating
// process, and a deserialized non-null `HeapPtr` would be undefined
// behavior on first deref. The borsh impls below therefore *fail* at the
// boundary instead of round-tripping to `null()`: every type the compiler
// actually puts into a serialized `Program` (`Object::{Function, Class,
// Enum, String}`) is HeapPtr-free, so any path that does encounter one
// reflects a real bug we want to surface immediately rather than mask.
impl BorshSerialize for HeapPtr {
fn serialize<W: std::io::Write>(&self, _writer: &mut W) -> std::io::Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"HeapPtr is a runtime-only pointer and must not be serialized",
))
}
}
impl BorshDeserialize for HeapPtr {
fn deserialize_reader<R: std::io::Read>(_reader: &mut R) -> std::io::Result<Self> {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"HeapPtr cannot be deserialized: runtime pointer leaked into serialized data",
))
}
}
impl PartialEq for HeapPtr {
fn eq(&self, other: &Self) -> bool {
self.ptr == other.ptr
}
}
impl Eq for HeapPtr {}
impl Default for HeapPtr {
fn default() -> Self {
Self::null()
}View on GitHub (pinned to bd85ce9dee)