{"record":{"id":"e043942e10636067","repo":"denoland/deno","slug":"cannot-construct-unsafecallback-cannot-be-nonbloc","errorCode":null,"errorMessage":"Cannot construct UnsafeCallback: cannot be nonblocking","messagePattern":"Cannot construct UnsafeCallback: cannot be nonblocking","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/ffi/00_ffi.js","lineNumber":405,"sourceCode":"    case \"isize\":\n      return [8, 8];\n    default:\n      throw new TypeError(`Cannot get pointer size, unsupported type: ${type}`);\n  }\n}\n\nclass UnsafeCallback {\n  #refcount;\n  // Internal promise only meant to keep Deno from exiting\n  #refpromise;\n  #rid;\n  definition;\n  callback;\n  pointer;\n\n  constructor(definition, callback) {\n    if (definition.nonblocking) {\n      throw new TypeError(\n        \"Cannot construct UnsafeCallback: cannot be nonblocking\",\n      );\n    }\n    const { 0: rid, 1: pointer } = op_ffi_unsafe_callback_create(\n      definition,\n      callback,\n    );\n    this.#refcount = 0;\n    this.#rid = rid;\n    this.pointer = pointer;\n    this.definition = definition;\n    this.callback = callback;\n  }\n\n  static threadSafe(definition, callback) {\n    const unsafeCallback = new UnsafeCallback(definition, callback);\n    unsafeCallback.ref();\n    return unsafeCallback;","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/ffi/00_ffi.js#L387-L423","documentation":"Deno.UnsafeCallback registers a native-to-JS trampoline for passing JS functions to FFI. Callbacks are invoked synchronously on whatever native thread calls them, so the nonblocking flag (which exists for outgoing calls made via UnsafeFnPointer/UnsafePointerFunction) has no meaning here and is rejected with a TypeError at construction.","triggerScenarios":"new Deno.UnsafeCallback({ parameters: [\"u32\"], result: \"void\", nonblocking: true }, cb).","commonSituations":"Reusing one shared definition object for both dlsym calls (where nonblocking is valid) and callbacks; copy-pasting a call definition into a callback registration; assuming all definition flags apply to every FFI API.","solutions":["Remove nonblocking from the callback definition","Keep separate definition constants for calls and for callbacks so flags do not leak across","If the callback body blocks, make the JS side dispatch work asynchronously (e.g. queue a task) instead of using the flag"],"exampleFix":"// before\nnew Deno.UnsafeCallback({ parameters: [], result: \"void\", nonblocking: true }, cb);\n\n// after\nnew Deno.UnsafeCallback({ parameters: [], result: \"void\" }, cb);","handlingStrategy":"validation","validationCode":"function makeUnsafeCallback(parameters, result, cb) {\n  const definition = { parameters, result }; // never carries nonblocking\n  return new Deno.UnsafeCallback(definition, cb);\n}","typeGuard":"function isCallbackDefinition(def: {\n  parameters: unknown[];\n  result: unknown;\n  nonblocking?: boolean;\n}): boolean {\n  return def.nonblocking !== true;\n}","tryCatchPattern":"try {\n  cb = new Deno.UnsafeCallback(definition, handler);\n} catch (err) {\n  if (err instanceof TypeError && err.message === \"Cannot construct UnsafeCallback: cannot be nonblocking\") {\n    const { nonblocking: _, ...rest } = definition;\n    cb = new Deno.UnsafeCallback(rest, handler);\n  } else throw err;\n}","preventionTips":["Keep dedicated definition objects for calls and for callbacks","Destructure out nonblocking before reusing a shared definition for a callback","Remember callbacks always run synchronously; make the JS handler dispatch async work itself"],"tags":["ffi","callback","nonblocking"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}