{"record":{"id":"eb2da4ad82ae736d","repo":"BoundaryML/baml","slug":"handle-type-mismatch","errorCode":null,"errorMessage":"handle type mismatch","messagePattern":"handle type mismatch","errorType":"error_code","errorClass":"HandleError","httpStatus":null,"severity":"error","filePath":"baml_language/crates/bridge_cffi/src/handle.rs","lineNumber":21,"sourceCode":"use 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!(\n            \"{field} contains an embedded NUL byte\"\n        )));\n    }","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/baml_language/crates/bridge_cffi/src/handle.rs#L3-L39","documentation":"HandleError::TypeMismatch from handle.rs. The handle is valid and live, but its handle_type does not match what the operation requires — e.g. passing a runtime handle where a media handle is expected. The bridge checks the type tag on every handle use.","triggerScenarios":"Passing a handle of the wrong kind to a typed operation (e.g. a function/runtime handle to a media API or vice versa); reusing one handle variable across differently-typed calls; bindings that lose the type distinction and use a single int everywhere.","commonSituations":"Copy-pasted call code reusing the wrong handle variable; untyped FFI bindings (plain integers) that make cross-typing easy; refactors that changed which object a handle refers to.","solutions":["Pass the handle kind the API expects — check which create_* call produced it.","Keep handles in distinct, typed variables instead of reusing one.","Check handle_type on the handle struct before the call.","Use strongly typed wrapper bindings rather than raw integers."],"exampleFix":"// before\nlet h = runtime_handle; // handle_type = RUNTIME\nmedia_read(h);\n// after\nlet h = media_handle;   // handle_type = MEDIA\nmedia_read(h);","handlingStrategy":"type-guard","validationCode":"if handle.handle_type != EXPECTED_HANDLE_TYPES['media_read']:\n    raise TypeError('wrong handle kind for media_read')","typeGuard":"def is_media_handle(handle) -> bool:\n    return handle.handle_type == HandleType.MEDIA","tryCatchPattern":"try:\n    result = bridge.media_read(handle)\nexcept BridgeError as e:\n    if e is HandleError.TypeMismatch:\n        log.error('passed wrong handle kind; check create_* origin')","preventionTips":["Keep one variable per handle kind","Wrap raw handle ints in typed wrapper classes","Check handle_type before any handle-consuming call"],"tags":["ffi","handle","type-mismatch"],"backgroundTag":"type-mismatch","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"}