denoland/deno · error · TypeError
ERR_INVALID_THIS
ERR_INVALID_THIS
Error message
Value of "this" must be of type Scheduler
What it means
The node:timers scheduler is a singleton; its methods validate the receiver by identity (self === scheduler) and throw ERR_INVALID_THIS for any other this. The constructor throws ERR_ILLEGAL_CONSTRUCTOR, so there is exactly one instance and prototype-linked or property-cloned look-alikes are rejected.
Source
Thrown at ext/node/polyfills/timers.ts:365
}
if (onCancel) {
signal?.removeEventListener("abort", onCancel);
}
}
}
const promises = {
setTimeout: setTimeoutPromise,
setImmediate: setImmediatePromise,
setInterval: setIntervalAsync,
};
// There is exactly one `scheduler`, so identity is the check: a
// prototype-linked object, or one carrying whatever properties the real
// instance has, is still a foreign receiver.
function validateScheduler(self: unknown) {
if (self !== scheduler) {
throw new ERR_INVALID_THIS("Scheduler");
}
}
class Scheduler {
constructor() {
throw new ERR_ILLEGAL_CONSTRUCTOR();
}
// Not `async`: the `this` check has to reject a foreign receiver
// synchronously rather than returning a rejected promise.
wait(
delay: number,
options?: { signal?: AbortSignal },
): Promise<void> {
validateScheduler(this);
return setTimeoutPromise(delay, undefined, options);
}
yield() {
validateScheduler(this);View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Always call through the object: scheduler.wait(100, { signal })
- Bind when detaching: const wait = scheduler.wait.bind(scheduler)
- Wrap in an arrow function: (delay, opts) => scheduler.wait(delay, opts)
Example fix
// before
const { wait } = scheduler;
wait(100); // throws ERR_INVALID_THIS
// after
const wait = scheduler.wait.bind(scheduler);
wait(100); Defensive patterns
Strategy: validation
Validate before calling
const schedulerWait = scheduler.wait.bind(scheduler);
// or wrap: const schedulerWait = (d: number, o?: { signal?: AbortSignal }) => scheduler.wait(d, o); Try / catch
try { scheduler.wait.call(scheduler, 100); } catch (e) { if (e.code === 'ERR_INVALID_THIS') throw new Error('scheduler methods must run on the scheduler instance'); else throw e; } Prevention
- Never destructure scheduler methods; call them as scheduler.wait(...)
- Bind once at module scope when passing the method around
- Wrap scheduler calls in small named helpers in your codebase
When it happens
Trigger: const { wait } = scheduler; wait(100) (detached method); passing the bare function as a callback: setTimeout(scheduler.wait, 10, 100); scheduler.wait.call({}, 100).
Common situations: Destructuring for convenience; wiring scheduler methods into generic dispatchers or option objects; wrapper functions losing the receiver.
Related errors
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/4d4ea7f1aac5d2f5.
Report an issue: GitHub.