{"record":{"id":"aa43814fa5d910b9","repo":"BoundaryML/baml","slug":"invalid-handle-key-0","errorCode":null,"errorMessage":"Invalid handle key: {0}","messagePattern":"Invalid handle key: (.+?)","errorType":"error_code","errorClass":"CtypesError","httpStatus":null,"severity":"error","filePath":"baml_language/crates/bridge_ctypes/src/error.rs","lineNumber":14,"sourceCode":"//! Error types used by the shared ctypes conversion logic.\n\nuse thiserror::Error;\n\n/// Errors that can occur during value encoding/decoding for the bridge.\n#[derive(Debug, Error)]\npub enum CtypesError {\n    #[error(\"Protobuf decode error: {0}\")]\n    ProtobufDecode(#[from] prost::DecodeError),\n\n    #[error(\"Null buffer pointer\")]\n    NullBuffer,\n\n    #[error(\"Invalid handle key: {0}\")]\n    InvalidHandleKey(u64),\n\n    #[error(\"Map entry missing key\")]\n    MapEntryMissingKey,\n\n    /// Carries only the input length, not the input itself — untrusted hex\n    /// blobs can be up to the FFI decode cap (~67M chars), and embedding\n    /// them in error messages bloats logs and exposes payload contents.\n    #[error(\"Invalid bigint hex string ({len} bytes)\")]\n    InvalidBigint { len: usize },\n\n    /// Carries only the input length for over-cap decimal type literals, so a\n    /// hostile descriptor cannot amplify logs by echoing its full payload.\n    #[error(\"Invalid decimal bigint literal ({len} bytes)\")]\n    InvalidBigintLiteral { len: usize },\n\n    #[error(\n        \"Invalid InboundValue.value_type: a root union or optional does not identify one exact selected type\"","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/BoundaryML/baml/blob/bd85ce9dee1463ff04d27efd20531013a4ff46c1/baml_language/crates/bridge_ctypes/src/error.rs#L1-L32","documentation":"CtypesError::InvalidHandleKey(u64) indicates a handle-table key passed across the bridge does not resolve to any live entry in HANDLE_TABLE. The key may be stale (already released), from another process, or simply wrong.","triggerScenarios":"Calling clone_handle/release_handle/media_* accessors with a key that was already released, never created, or outlived its table row; using a key from a previous bridge session.","commonSituations":"Double-free of handles (releasing twice); lifetime bugs where the host dropped a handle but still uses it; serializing keys across process restarts; ID reuse assumptions after upgrades.","solutions":["Keep the handle alive for as long as you use the key; call release_handle exactly once per ownership.","Re-acquire the handle (e.g. media_from_url/... ) if it was released; do not cache keys across sessions.","Log the key and check live_handle_count/refcount instrumentation to confirm it is live.","Search for double-release paths in the host code (release on both success and error paths)."],"exampleFix":"// before\nrelease_handle(key);  # ok\nrelease_handle(key);  # InvalidHandleKey / invalid handle\n// after\nif not released.contains(key):\n    release_handle(key)\n    released.add(key)","handlingStrategy":"try-catch","validationCode":"# check liveness before use (test instrumentation)\nassert bridge.live_handle_count() > 0 and key in live_keys","typeGuard":null,"tryCatchPattern":"try:\n    media = bridge.media_url(key, handle_type)\nexcept BridgeError as e:\n    if str(e).startswith(\"Invalid handle key\"):\n        media = reacquire_media()  # re-mint the handle\n    else:\n        raise","preventionTips":["Release each handle exactly once; guard against double-release in error paths","Never persist handle keys across process restarts or library reloads","Model handles as opaque owned objects in host code, not raw integers"],"tags":["ffi","handle","lifetime","rust"],"backgroundTag":"resource-not-found","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"}