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

  1. Always call through the object: scheduler.wait(100, { signal })
  2. Bind when detaching: const wait = scheduler.wait.bind(scheduler)
  3. 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

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.