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
- Bind extracted methods: `const post = port.postMessage.bind(port)`.
- Wrap in an arrow function: `const post = (...a) => port.postMessage(...a)`.
- Invoke methods as member expressions on the port instance itself.
- 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
- Bind methods when extracting them: `port.postMessage.bind(port)`.
- Invoke MessagePort methods as member expressions (port.close(), port.start()).
- Never wrap ports in Proxy or substitute mock objects for real entangled ports.
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
- Illegal invocation
- ERR_INVALID_THIS
- ERR_CLOSED_MESSAGE_PORT
- MessageEvent constructor: eventInitDict.ports (${formatForRa
- MessageEvent constructor: Expected eventInitDict.ports[${i}]
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/258f59a2be0be599.
Report an issue: GitHub.