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
- Filter self before sending: ids.filter((id) => id !== threadId).
- Check worker_threads.isMainThread / threadId when ids come from config or discovery.
- 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
- Maintain peer registries that never contain your own threadId.
- Remember the main thread is id 0 when writing bidirectional code.
- Await or .catch every postMessageToThread call — failures arrive as rejections.
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
- ERR_INSPECTOR_NOT_WORKER
- Unable to deserialize RecordableHistogram
- ERR_WORKER_UNSUPPORTED_OPERATION
- ERR_WORKER_UNSUPPORTED_OPERATION
- ERR_WORKER_INVALID_EXEC_ARGV
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/ffcdefa5d66c9711.
Report an issue: GitHub.