{"record":{"id":"5445fef4bf60f4b3","repo":"BoundaryML/baml","slug":"invalid-handle","errorCode":null,"errorMessage":"invalid handle","messagePattern":"invalid handle","errorType":"exception","errorClass":"HandleError","httpStatus":null,"severity":"error","filePath":"baml_language/crates/bridge_cffi/src/handle.rs","lineNumber":18,"sourceCode":"//! Safe, target-neutral ordinary handle and media operations.\n\nuse std::sync::Arc;\n\nuse bex_project::{BexExternalAdt, MediaKind, MediaValue};\nuse bridge_ctypes::{CffiHandleTableEntry, HANDLE_TABLE, baml_bridge::cffi::BamlHandleType};\n\n/// An owned handle-table key and its protocol type tag.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub struct HandleParts {\n    pub key: u64,\n    pub handle_type: i32,\n}\n\n/// Failure from a safe ordinary handle or media operation.\n#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]\npub enum HandleError {\n    #[error(\"invalid handle\")]\n    InvalidHandle,\n    #[error(\"handle type mismatch\")]\n    TypeMismatch,\n    #[error(\"unsupported handle type\")]\n    UnsupportedHandleType,\n    #[error(\"{0}\")]\n    InvalidInput(String),\n}\n\nfn insert_entry(entry: CffiHandleTableEntry) -> HandleParts {\n    let handle_type = entry.handle_type() as i32;\n    let key = HANDLE_TABLE.insert(entry);\n    HandleParts { key, handle_type }\n}\n\nfn validate_input(value: &str, field: &str) -> Result<(), HandleError> {\n    if value.contains('\\0') {\n        return Err(HandleError::InvalidInput(format!(","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/baml_language/crates/bridge_cffi/src/handle.rs#L1-L36","documentation":"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.","triggerScenarios":"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.","commonSituations":"Using a handle after runtime shutdown or handle_close; garbage/zero-initialized ctypes structs; holding handles across process boundaries or serializing them.","solutions":["Re-acquire a fresh handle from the API that originally created it.","Check the handle was not already closed/released before reuse.","Ensure the runtime instance that issued the handle is still alive.","Verify ctypes struct layout matches the Rust definition so the id is not corrupted."],"exampleFix":"# before\nhandle = 0  # zeroed struct default\nmedia_read(handle)\n# after\nhandle = runtime.create_media(path)\nmedia_read(handle)","handlingStrategy":"type-guard","validationCode":"if handle.id == 0 or handle.id not in issued_handles:\n    raise ValueError('stale or invalid handle')","typeGuard":"def is_live_handle(handle, issued: set) -> bool:\n    return handle.id != 0 and handle.id in issued","tryCatchPattern":"try:\n    result = bridge.media_read(handle)\nexcept BridgeError as e:\n    if e is HandleError.InvalidHandle:\n        handle = reacquire_handle(source)\n        result = bridge.media_read(handle)","preventionTips":["Never cache handles beyond the runtime's lifetime","Close handles exactly once and drop references afterwards","Verify ctypes struct layouts against the Rust definitions"],"tags":["ffi","handle","lifetime"],"backgroundTag":"invalid-argument-value","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"}