denoland/deno · error · TypeError

ERR_INVALID_THIS

ERR_INVALID_THIS

Error message

Value of "this" must be of type EventEmitterReferencingAsyncResource

What it means

The internal class EventEmitterReferencingAsyncResource (built lazily by getEventEmitterAsyncResource) stores the emitter in the kEventEmitter symbol property set by its constructor; its eventEmitter getter throws ERR_INVALID_THIS at _events.mjs:1244 when that property is undefined. That only happens when the getter runs with a receiver that never executed the constructor - i.e. the getter was detached from a real instance and invoked with the wrong 'this'.

Source

Thrown at ext/node/polyfills/_events.mjs:1244

const kAsyncResource = Symbol("kAsyncResource");
const kEventEmitter = Symbol("kEventEmitter");

// EventEmitterAsyncResource and its helper class are defined lazily to avoid
// eagerly loading node:async_hooks (which provides AsyncResource).
let _EventEmitterAsyncResource;
function getEventEmitterAsyncResource() {
  if (_EventEmitterAsyncResource) return _EventEmitterAsyncResource;
  const { AsyncResource } = lazyAsyncHooks();

  class EventEmitterReferencingAsyncResource extends AsyncResource {
    constructor(ee, type, options) {
      super(type, options);
      this[kEventEmitter] = ee;
    }

    get eventEmitter() {
      if (this[kEventEmitter] === undefined) {
        throw new ERR_INVALID_THIS("EventEmitterReferencingAsyncResource");
      }
      return this[kEventEmitter];
    }
  }

  _EventEmitterAsyncResource = class EventEmitterAsyncResource
    extends EventEmitter {
    constructor(options = undefined) {
      let name;
      if (typeof options === "string") {
        name = options;
        options = undefined;
      } else {
        if (new.target === _EventEmitterAsyncResource) {
          validateString(options?.name, "options.name");
        }
        name = options?.name || new.target.name;
      }

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Always access the property on the instance itself: resource.eventEmitter, never a detached getter
  2. Do not destructure getters; read them directly or via a helper that keeps the receiver
  3. In subclasses, always call super(...) so the internal symbol properties are installed
  4. Verify the receiver with instanceof before touching the getter in generic code

Example fix

// before
const { eventEmitter } = asyncResource; // 'this' lost when invoked later
eventEmitter.emit('x');

// after
asyncResource.eventEmitter.emit('x');
Defensive patterns

Strategy: type-guard

Validate before calling

const { EventEmitterAsyncResource } = require('node:events');

if (!(resource instanceof EventEmitterAsyncResource)) {
  throw new TypeError('expected EventEmitterAsyncResource');
}
const ee = resource.eventEmitter;

Type guard

const isEEAR = (v) =>
  v instanceof require('node:events').EventEmitterAsyncResource;

Prevention

When it happens

Trigger: Extracting the getter and calling it bare or via .call(): const { eventEmitter } = asyncResource; or Object.getOwnPropertyDescriptor(proto, 'eventEmitter').get.call({}); calling the getter through Reflect.apply with a foreign receiver; a subclass that overrides the constructor without calling super() so kEventEmitter is never assigned; accessing .eventEmitter on an object created via Object.create(proto) instead of new.

Common situations: Destructuring-style bugs (const { eventEmitter } = resource); wrapper or proxy code that rebinds getters; copy-style clones that copy accessors but not symbol properties; async-hooks experimentation.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/dd462b0130e0b169. Report an issue: GitHub.