denoland/deno · error · Error

ERR_WORKER_MESSAGING_SAME_THREAD

ERR_WORKER_MESSAGING_SAME_THREAD

Error message

Cannot send a message to the same thread.

What it means

Cross-thread messaging refuses self-delivery: when targetThreadId equals the caller's own worker_threads.threadId, postMessageToThread throws ERR_WORKER_MESSAGING_SAME_THREAD. Validation runs inside a try block whose catch converts the throw into a rejected promise, so the failure surfaces as an unhandled rejection unless awaited or caught.

Source

Thrown at ext/node/polyfills/worker_threads.ts:1626

      transferList = transferListOrTimeout;
      timeout = maybeTimeout;
    } else if (typeof transferListOrTimeout === "number") {
      timeout = transferListOrTimeout;
    } else if (transferListOrTimeout !== undefined) {
      throw new ERR_INVALID_ARG_TYPE(
        "transferList",
        "Array",
        transferListOrTimeout,
      );
    }
    if (timeout !== undefined) {
      validateInteger(timeout, "timeout", 0);
    }
    if (transferList !== undefined) {
      validateArray(transferList, "transferList");
    }
    if (targetThreadId === threadId) {
      throw new ERR_WORKER_MESSAGING_SAME_THREAD();
    }
  } catch (err) {
    return PromiseReject(err);
  }

  // Senders also need to be registered so the destination's ack can
  // find them. Cheap no-op after the first call.
  ensureCrossThreadMessaging();

  const id = nextThreadMessageId++;
  let data;
  try {
    const envelope = {
      __nodeWorkerMessage: true,
      sender: threadId,
      id,
      kind: kMessageKindData,
      payload: value,

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Filter self before sending: ids.filter((id) => id !== threadId).
  2. Check worker_threads.isMainThread / threadId when ids come from config or discovery.
  3. Catch the rejection at the call site when ids are untrusted.

Example fix

// before
for (const id of allThreadIds) postMessageToThread(id, msg);

// after
import { postMessageToThread, threadId } from 'node:worker_threads';
for (const id of allThreadIds) {
  if (id !== threadId) postMessageToThread(id, msg);
}
Defensive patterns

Strategy: validation

Validate before calling

import { postMessageToThread, threadId } from 'node:worker_threads';

function broadcast(ids: number[], msg: unknown) {
  for (const id of ids) {
    if (id !== threadId) postMessageToThread(id, msg);
  }
}

Type guard

const isPeerThread = (id: number) => id !== threadId;

Try / catch

postMessageToThread(id, msg).catch((e) => {
  if (e?.code !== 'ERR_WORKER_MESSAGING_SAME_THREAD') throw e;
});

Prevention

When it happens

Trigger: postMessageToThread(threadId, msg) — literal self-send; a broadcast loop over all discovered thread ids that does not exclude the sender; main-thread code (threadId 0) sending to id 0.

Common situations: Peer-discovery fan-out in worker pools; hardcoded ids where 0 (main thread) collides with the sender; porting pub/sub code where self-subscription is normal.

Related errors


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