{"record":{"id":"d4c3d79af0c49d28","repo":"denoland/deno","slug":"unable-to-deserialize-result-parameter","errorCode":null,"errorMessage":"Unable to deserialize result parameter.","messagePattern":"Unable to deserialize result parameter\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ext/ffi/callback.rs","lineNumber":551,"sourceCode":"        {\n          let byte_offset = value.byte_offset();\n          let ab = value\n            .buffer(scope)\n            .expect(\"Unable to deserialize result parameter.\");\n          size = value.byte_length();\n          ab.data()\n            .expect(\"Unable to deserialize result parameter.\")\n            .as_ptr()\n            .add(byte_offset)\n        } else if let Ok(value) = v8::Local::<v8::ArrayBuffer>::try_from(value)\n        {\n          size = value.byte_length();\n          value\n            .data()\n            .expect(\"Unable to deserialize result parameter.\")\n            .as_ptr()\n        } else {\n          panic!(\"Unable to deserialize result parameter.\");\n        };\n        std::ptr::copy_nonoverlapping(\n          pointer as *mut u8,\n          result as *mut u8,\n          std::cmp::min(size, (*cif.rtype).size),\n        );\n      }\n      NativeType::Void => {\n        // nop\n      }\n    };\n  }\n}\n\n#[op2]\npub fn op_ffi_unsafe_callback_ref(\n  state: Rc<RefCell<OpState>>,\n  #[smi] rid: ResourceId,","sourceCodeStart":533,"sourceCodeEnd":569,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/ffi/callback.rs#L533-L569","documentation":"For Deno FFI callbacks (Deno.UnsafeCallback) whose native result type is a pointer or buffer, the value returned by the JS callback must be a typed array or ArrayBuffer so its data pointer can be copied to the native caller. If the callback returns anything else for such a result type (undefined, null, plain object, string), the FFI bridge panics with this message. The panic happens across the native boundary, so a JS try/catch cannot intercept it.","triggerScenarios":"A Deno.UnsafeCallback declared with result: \"buffer\" (or another pointer-backed result type) whose JS function returns undefined/null/a plain value on at least one code path; returning a number is reinterpreted as a raw pointer, which is usually a second bug waiting to happen.","commonSituations":"Porting C callback contracts to Deno FFI; error branches in the JS callback that forget to return a buffer; refactors changing what the callback returns while the FFI signature stays the same.","solutions":["Make the callback return a Uint8Array (or matching TypedArray) for buffer results on every code path, including error branches","Use `result: \"pointer\"` with a BigInt address only when you truly manage that memory yourself","Return `new Uint8Array(0)` instead of null when there is no data","Cover every callback branch in tests; the panicking path is the one you did not test"],"exampleFix":"// before — buffer result, but the error branch returns undefined\nconst cb = new Deno.UnsafeCallback(\n  { parameters: [\"i32\"], result: \"buffer\" },\n  (code: number) => code > 0 ? new Uint8Array([code]) : null,\n);\n\n// after — every path returns a TypedArray\nconst cb = new Deno.UnsafeCallback(\n  { parameters: [\"i32\"], result: \"buffer\" },\n  (code: number) => new Uint8Array([code & 0xff]),\n);","handlingStrategy":"type-guard","validationCode":"const cb = new Deno.UnsafeCallback(\n  { parameters: [], result: \"buffer\" },\n  () => {\n    const v = compute();\n    return v instanceof Uint8Array ? v : new Uint8Array(0); // never panic the bridge\n  },\n);","typeGuard":"function isFFIBufferResult(v: unknown): v is Uint8Array | ArrayBuffer {\n  return v instanceof ArrayBuffer ||\n    (ArrayBuffer.isView(v) && !(v instanceof DataView));\n}","tryCatchPattern":null,"preventionTips":["Every code path of a buffer-result callback must return a TypedArray or ArrayBuffer","Use `new Uint8Array(0)` for empty results instead of null/undefined","Return a BigInt only for explicitly declared pointer results","Cover error branches in callback tests — the untested branch is where the panic lives"],"tags":["ffi","unsafe-callback","type-mismatch","panic"],"backgroundTag":"ffi-callback-return-type-mismatch","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}