denoland/deno · error · TypeError
expected a function
Error message
expected a function
What it means
Deno's core bootstrap re-wraps queueMicrotask so that exceptions raised inside the microtask are routed to the runtime's exception reporting instead of disappearing. Before enqueueing anything, the wrapper enforces the platform contract: the callback must be a function, otherwise it throws TypeError('expected a function'). This lives in libs/core/01_core.js and covers every queueMicrotask call routed through the core runtime.
Source
Thrown at libs/core/01_core.js:583
// V8 skips experimental globals while snapshotting, so the native function is
// only installed at runtime. In a from-scratch runtime it's already on the
// global when this module runs (before our wrapper replaces it below), so we
// can grab it directly. When restoring from a snapshot it isn't there yet, so
// fall back to a lazy capture on first use: our wrapper shadows the native one
// as an own property of the global, so momentarily remove our property to
// reveal the native one underneath, then restore our wrapper.
let nativeQueueMicrotask = window.queueMicrotask;
function captureNativeQueueMicrotask() {
const wrapper = window.queueMicrotask;
delete window.queueMicrotask;
nativeQueueMicrotask = window.queueMicrotask;
window.queueMicrotask = wrapper;
return nativeQueueMicrotask;
}
function queueMicrotask(cb) {
if (typeof cb != "function") {
throw new TypeError("expected a function");
}
const enqueue = nativeQueueMicrotask ?? captureNativeQueueMicrotask();
return enqueue(() => {
try {
cb();
} catch (error) {
reportExceptionCallback(error);
}
});
}
// Some "extensions" rely on "BadResource", "Interrupted", "NotCapable"
// errors in the JS code (eg. "deno_net") so they are provided in "Deno.core"
// but later reexported on "Deno.errors"
class BadResource extends Error {
constructor(msg, options) {
super(msg, options);
this.name = "BadResource";View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Pass a function reference or wrap the call: `queueMicrotask(() => fn())`
- Default optional callbacks: `queueMicrotask(cb ?? (() => {}))`, or skip the call when cb is missing
- Guard with `typeof cb === 'function'` before enqueueing
Example fix
// before queueMicrotask(flushQueue()); // passes flushQueue()'s undefined return value // after queueMicrotask(() => flushQueue());
Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof cb !== 'function') {
throw new TypeError('expected a function');
}
queueMicrotask(cb); Type guard
const isFunction = (v) => typeof v === 'function'; // usage: isFunction(cb) ? queueMicrotask(cb) : fallback();
Prevention
- Never invoke the callback while passing it — pass the reference or wrap it in an arrow
- Default optional callbacks: cb ?? (() => {}), or skip the call when absent
- Type the parameter as () => void so the compiler flags non-function arguments
When it happens
Trigger: Calling queueMicrotask with a non-function: `queueMicrotask(fn())` executes fn immediately and passes its (often undefined) return value; passing an optional callback that was never supplied; passing an object holding a callable field instead of the function itself.
Common situations: Immediately-invoked setup calls that return undefined; APIs shaped as `queueMicrotask(maybeCb)` where maybeCb is undefined; passing results of array operations that produced no function.
Related errors
- Failed to construct 'PerformanceObserver': The callback prov
- Expected the second argument to assertSnapshot() to be an op
- Snapshot serializer must return a string
- Cannot access pointer: expected 'ArrayBuffer', 'SharedArrayB
- Deno.Kv can not be constructed: use Deno.openKv instead
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/f24588e974a2d21f.
Report an issue: GitHub.