denoland/deno · error · TypeError

Illegal invocation

Error message

Illegal invocation

What it means

MessagePort methods resolve their receiver through the module-private `messagePortIds` WeakMap (ext/web/13_message_port.js:150). When a method runs with a `this` that is not a live runtime-created MessagePort, the WeakMap lookup returns undefined and Deno throws TypeError "Illegal invocation" — the same semantics as the WebIDL brand check. It means the receiver never had (or no longer resolves to) an entangled port id.

Source

Thrown at ext/web/13_message_port.js:150

const _messageEventListenerCount = Symbol("messageEventListenerCount");
const nodeWorkerThreadCloseCb = Symbol("nodeWorkerThreadCloseCb");
const nodeWorkerThreadCloseCbInvoked = Symbol("nodeWorkerThreadCloseCbInvoked");
const refMessagePort = Symbol("refMessagePort");
/** It is used by 99_main.js and worker_threads to
 * unref/ref on the global message event handler count. */
const unrefParentPort = Symbol("unrefParentPort");

/** @type {WeakMap<MessagePort, number | null>} */
const messagePortIds = new SafeWeakMap();

/**
 * @param {MessagePort} port
 * @returns {number | null}
 */
function getMessagePortId(port) {
  const id = WeakMapPrototypeGet(messagePortIds, port);
  if (id === undefined) {
    throw new TypeError("Illegal invocation");
  }
  return id;
}

/**
 * @param {MessagePort} port
 * @param {number | null} id
 */
function setMessagePortId(port, id) {
  WeakMapPrototypeSet(messagePortIds, port, id);
}

/**
 * @param {MessagePort} port
 * @returns {number | null}
 */
function takeMessagePortId(port) {
  const id = getMessagePortId(port);

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Bind extracted methods: `const post = port.postMessage.bind(port)`.
  2. Wrap in an arrow function: `const post = (...a) => port.postMessage(...a)`.
  3. Invoke methods as member expressions on the port instance itself.
  4. Verify `port instanceof MessagePort` before use and never substitute mocks for real entangled ports.

Example fix

// before
const send = port.postMessage;
send(data); // TypeError: Illegal invocation

// after
const send = port.postMessage.bind(port);
send(data);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(port instanceof MessagePort)) {
  throw new TypeError("A real MessagePort instance is required");
}
port.postMessage(data);

Type guard

const isMessagePort = (v) => v instanceof MessagePort;

Prevention

When it happens

Trigger: Detaching the method from its owner: `const post = port.postMessage; post('x')`; calling `MessagePort.prototype.postMessage.call(fakeThis, ...)`; invoking methods on a mock, Proxy wrapper, or hand-crafted object that satisfies the brand check but was never registered in the WeakMap.

Common situations: Passing `port.postMessage` as a bare callback (e.g. `items.forEach(port.postMessage)`); dependency-injection test doubles replacing MessagePort; wrapping ports in Proxy for logging; code copied between realms/workers.

Related errors


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