BoundaryML/baml · error

Sentinel cannot be serialized

Error message

Sentinel cannot be serialized

What it means

The VM value serializer in bex_vm_types refuses to serialize an Object variant carrying a Sentinel. Sentinel objects are internal bookkeeping markers (e.g. heap-debug probes), not real data, so a serialized Sentinel could never be deserialized into a meaningful value. Serialization is deliberately made to fail loudly with InvalidData rather than silently emit a lossy byte stream. The same guard covers HostClosure, which cannot cross the wire either.

Source

Thrown at baml_language/crates/bex_vm_types/src/types/object.rs:270

            Self::Float(v) => ObjectWire::Float(*v),
            Self::Future(v) => ObjectWire::Future(v.clone()),
            Self::UnscheduledFuture(v) => ObjectWire::UnscheduledFuture(v.clone()),
            Self::Type(v) => ObjectWire::Type(Box::new(v.ty.clone())),
            Self::RustData(_) => {
                return Err(std::io::Error::new(
                    std::io::ErrorKind::InvalidData,
                    "RustData cannot be serialized",
                ));
            }
            Self::HostClosure(_) => {
                return Err(std::io::Error::new(
                    std::io::ErrorKind::InvalidData,
                    "HostClosure cannot be serialized",
                ));
            }
            #[cfg(feature = "heap_debug")]
            Self::Sentinel(_) => {
                return Err(std::io::Error::new(
                    std::io::ErrorKind::InvalidData,
                    "Sentinel cannot be serialized",
                ));
            }
        };
        proxy.serialize(writer)
    }
}

impl BorshDeserialize for Object {
    fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
        let proxy = ObjectWire::deserialize_reader(reader)?;
        Ok(match proxy {
            ObjectWire::Function(v) => Self::Function(v),
            ObjectWire::Interface(v) => Self::Interface(v),
            ObjectWire::Package(v) => Self::Package(v),
            ObjectWire::ImplRule(v) => Self::ImplRule(v),
            ObjectWire::Class(v) => Self::Class(v),

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Filter out or unwrap Sentinel entries before serializing (serialize only real payload objects).
  2. If Sentinel is legitimately needed, serialize a lightweight placeholder (e.g. unit/null marker) and reconstruct it on deserialize instead of routing it into this arm.
  3. Disable the heap_debug feature for production serialization paths, where the Sentinel arm does not exist.
  4. If you only need the serialized payload, call the inner proxy's serialize directly, skipping the enclosing object wrapper.

Example fix

// before
for obj in heap.objects() {
    obj.serialize(&mut out)?;
}
// after
for obj in heap.objects() {
    if matches!(obj, Object::Sentinel(_)) { continue; }
    obj.serialize(&mut out)?;
}
Defensive patterns

Strategy: validation

Validate before calling

fn is_serializable(obj: &Object) -> bool {
    !matches!(obj, Object::Sentinel(_) | Object::HostClosure(_))
}

Type guard

fn as_serializable(obj: &Object) -> Option<&Object> {
    match obj {
        Object::Sentinel(_) | Object::HostClosure(_) => None,
        _ => Some(obj),
    }
}

Prevention

When it happens

Trigger: Calling serialize on an Object::Sentinel(_) value — typically via object.serialize(writer) or a Value serialization that reaches this match arm in types/object.rs. Only compiled with feature = "heap_debug". Any heap walk or debug dump that accidentally includes Sentinel objects and pipes them through the serialization path triggers it.

Common situations: Debug builds with the heap_debug feature enabled where a heap snapshot/serializer walks all objects and does not filter internal sentinels; tooling that serializes whole VM heaps or object graphs for inspection; tests that put a sentinel marker inside an object field and then round-trip the value.

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


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