BoundaryML/baml · error · HandleError

invalid handle

Error message

invalid handle

What it means

HandleError::InvalidHandle from handle.rs. The bridge received a handle value that does not refer to any live object in the handle table — it was never issued, was already closed/freed, or was corrupted at the FFI boundary. Raised for safe ordinary-handle and media operations.

Source

Thrown at baml_language/crates/bridge_cffi/src/handle.rs:18

//! Safe, target-neutral ordinary handle and media operations.

use std::sync::Arc;

use bex_project::{BexExternalAdt, MediaKind, MediaValue};
use bridge_ctypes::{CffiHandleTableEntry, HANDLE_TABLE, baml_bridge::cffi::BamlHandleType};

/// An owned handle-table key and its protocol type tag.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct HandleParts {
    pub key: u64,
    pub handle_type: i32,
}

/// Failure from a safe ordinary handle or media operation.
#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
pub enum HandleError {
    #[error("invalid handle")]
    InvalidHandle,
    #[error("handle type mismatch")]
    TypeMismatch,
    #[error("unsupported handle type")]
    UnsupportedHandleType,
    #[error("{0}")]
    InvalidInput(String),
}

fn insert_entry(entry: CffiHandleTableEntry) -> HandleParts {
    let handle_type = entry.handle_type() as i32;
    let key = HANDLE_TABLE.insert(entry);
    HandleParts { key, handle_type }
}

fn validate_input(value: &str, field: &str) -> Result<(), HandleError> {
    if value.contains('\0') {
        return Err(HandleError::InvalidInput(format!(

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Re-acquire a fresh handle from the API that originally created it.
  2. Check the handle was not already closed/released before reuse.
  3. Ensure the runtime instance that issued the handle is still alive.
  4. Verify ctypes struct layout matches the Rust definition so the id is not corrupted.

Example fix

# before
handle = 0  # zeroed struct default
media_read(handle)
# after
handle = runtime.create_media(path)
media_read(handle)
Defensive patterns

Strategy: type-guard

Validate before calling

if handle.id == 0 or handle.id not in issued_handles:
    raise ValueError('stale or invalid handle')

Type guard

def is_live_handle(handle, issued: set) -> bool:
    return handle.id != 0 and handle.id in issued

Try / catch

try:
    result = bridge.media_read(handle)
except BridgeError as e:
    if e is HandleError.InvalidHandle:
        handle = reacquire_handle(source)
        result = bridge.media_read(handle)

Prevention

When it happens

Trigger: Passing a stale or already-released handle to a handle/media API; passing a fabricated or zeroed handle value across ctypes; using a handle after the runtime that owns it was destroyed.

Common situations: Using a handle after runtime shutdown or handle_close; garbage/zero-initialized ctypes structs; holding handles across process boundaries or serializing them.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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