denoland/deno · error · TypeError

MessageEvent constructor: Expected eventInitDict.ports[${i}]

Error message

MessageEvent constructor: Expected eventInitDict.ports[${i}] (${formatForBranded(p)}) to be an instance of MessagePort.

What it means

After the iterability check, MessageEvent's constructor iterates eventInitDict.ports and verifies every element against the web MessagePort prototype (ext/web/02_event.js:1366-1377). An element that is null, a primitive, or not branded as a MessagePort throws TypeError 'Expected eventInitDict.ports[i] ... to be an instance of MessagePort' with the offending index. Deno iterates using the user's own iterator so exotic iterables still work — only the element type is enforced.

Source

Thrown at ext/web/02_event.js:1374

            formatForRaw(ports)
          }) is not iterable.`,
        );
      }
      const MessagePortProto = getMessagePortPrototype();
      const arr = [];
      let i = 0;
      // Iterate using the user's own iterator so values that aren't real
      // arrays (e.g. a `RegExp` with a custom `Symbol.iterator` -- covered
      // by WPT's no-regexp-special-casing test) still produce the
      // expected `ports` array. SafeArrayIterator can't be used here
      // because it walks the value as if it were an Array.
      // deno-lint-ignore deno-internal/prefer-primordials
      for (const p of ports) {
        if (
          p === null || typeof p !== "object" ||
          !ObjectPrototypeIsPrototypeOf(MessagePortProto, p)
        ) {
          throw new TypeError(
            `MessageEvent constructor: Expected eventInitDict.ports[${i}] (${
              formatForBranded(p)
            }) to be an instance of MessagePort.`,
          );
        }
        arr[i++] = p;
      }
      // `ports` is a FrozenArray<MessagePort> per the HTML spec.
      this.ports = ObjectFreeze(arr);
    }
    // origin and lastEventId are USVString per spec, so coerce to string
    // (Node's test passes numbers and expects string coercion).
    this.origin = eventInitDict?.origin === undefined
      ? ""
      : String(eventInitDict.origin);
    this.lastEventId = eventInitDict?.lastEventId === undefined
      ? ""
      : String(eventInitDict.lastEventId);

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Filter the array to real web MessagePorts before constructing: ports.filter((p) => p instanceof MessagePort).
  2. Pass only ports obtained from MessageChannel or a worker/web-worker onmessage event — never node:worker_threads ports.
  3. Remove null/undefined placeholders instead of leaving holes in ports.

Example fix

// before
new MessageEvent('message', { data, ports: [port1, null] });
// TypeError: Expected eventInitDict.ports[1] (null) to be an instance of MessagePort.

// after
new MessageEvent('message', { data, ports: [port1] });
Defensive patterns

Strategy: type-guard

Validate before calling

const ports = rawPorts.filter((p) => p instanceof MessagePort);
new MessageEvent('message', { data, ports });

Type guard

const areAllMessagePorts = (a) =>
  Array.isArray(a) && a.every((p) => p instanceof MessagePort);

Prevention

When it happens

Trigger: new MessageEvent('message', { data, ports: [new MessageChannel().port1, null] }); arrays containing a node:worker_threads port, a number, a string, or a plain object; sparse arrays with holes (undefined elements).

Common situations: Port arrays built with placeholders/sentinels (null) for closed or unused channels; mixing Node worker_threads MessagePort objects into web MessageEvent construction; arrays produced by concat/map that accidentally inject undefined.

Related errors


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