denoland/deno · error · TypeError
Cannot construct UnsafeCallback: cannot be nonblocking
Error message
Cannot construct UnsafeCallback: cannot be nonblocking
What it means
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.
Source
Thrown at ext/ffi/00_ffi.js:405
case "isize":
return [8, 8];
default:
throw new TypeError(`Cannot get pointer size, unsupported type: ${type}`);
}
}
class UnsafeCallback {
#refcount;
// Internal promise only meant to keep Deno from exiting
#refpromise;
#rid;
definition;
callback;
pointer;
constructor(definition, callback) {
if (definition.nonblocking) {
throw new TypeError(
"Cannot construct UnsafeCallback: cannot be nonblocking",
);
}
const { 0: rid, 1: pointer } = op_ffi_unsafe_callback_create(
definition,
callback,
);
this.#refcount = 0;
this.#rid = rid;
this.pointer = pointer;
this.definition = definition;
this.callback = callback;
}
static threadSafe(definition, callback) {
const unsafeCallback = new UnsafeCallback(definition, callback);
unsafeCallback.ref();
return unsafeCallback;View on GitHub (pinned to 89f33cbef2)
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
Example fix
// before
new Deno.UnsafeCallback({ parameters: [], result: "void", nonblocking: true }, cb);
// after
new Deno.UnsafeCallback({ parameters: [], result: "void" }, cb); Defensive patterns
Strategy: validation
Validate before calling
function makeUnsafeCallback(parameters, result, cb) {
const definition = { parameters, result }; // never carries nonblocking
return new Deno.UnsafeCallback(definition, cb);
} Type guard
function isCallbackDefinition(def: {
parameters: unknown[];
result: unknown;
nonblocking?: boolean;
}): boolean {
return def.nonblocking !== true;
} Try / catch
try {
cb = new Deno.UnsafeCallback(definition, handler);
} catch (err) {
if (err instanceof TypeError && err.message === "Cannot construct UnsafeCallback: cannot be nonblocking") {
const { nonblocking: _, ...rest } = definition;
cb = new Deno.UnsafeCallback(rest, handler);
} else throw err;
} Prevention
- 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
When it happens
Trigger: new Deno.UnsafeCallback({ parameters: ["u32"], result: "void", nonblocking: true }, cb).
Common situations: 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.
Related errors
- Cannot access pointer: expected 'ArrayBuffer', 'SharedArrayB
- Cannot get pointer size: found recursive struct
- Cannot get pointer size, unsupported type: ${type}
- Foreign symbol of type 'void' is not supported
- No callback function supplied
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/e043942e10636067.
Report an issue: GitHub.