{"record":{"id":"ffcdefa5d66c9711","repo":"denoland/deno","slug":"err-worker-messaging-same-thread","errorCode":"ERR_WORKER_MESSAGING_SAME_THREAD","errorMessage":"Cannot send a message to the same thread.","messagePattern":"Cannot send a message to the same thread\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/worker_threads.ts","lineNumber":1626,"sourceCode":"      transferList = transferListOrTimeout;\n      timeout = maybeTimeout;\n    } else if (typeof transferListOrTimeout === \"number\") {\n      timeout = transferListOrTimeout;\n    } else if (transferListOrTimeout !== undefined) {\n      throw new ERR_INVALID_ARG_TYPE(\n        \"transferList\",\n        \"Array\",\n        transferListOrTimeout,\n      );\n    }\n    if (timeout !== undefined) {\n      validateInteger(timeout, \"timeout\", 0);\n    }\n    if (transferList !== undefined) {\n      validateArray(transferList, \"transferList\");\n    }\n    if (targetThreadId === threadId) {\n      throw new ERR_WORKER_MESSAGING_SAME_THREAD();\n    }\n  } catch (err) {\n    return PromiseReject(err);\n  }\n\n  // Senders also need to be registered so the destination's ack can\n  // find them. Cheap no-op after the first call.\n  ensureCrossThreadMessaging();\n\n  const id = nextThreadMessageId++;\n  let data;\n  try {\n    const envelope = {\n      __nodeWorkerMessage: true,\n      sender: threadId,\n      id,\n      kind: kMessageKindData,\n      payload: value,","sourceCodeStart":1608,"sourceCodeEnd":1644,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/worker_threads.ts#L1608-L1644","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nfor (const id of allThreadIds) postMessageToThread(id, msg);\n\n// after\nimport { postMessageToThread, threadId } from 'node:worker_threads';\nfor (const id of allThreadIds) {\n  if (id !== threadId) postMessageToThread(id, msg);\n}","handlingStrategy":"validation","validationCode":"import { postMessageToThread, threadId } from 'node:worker_threads';\n\nfunction broadcast(ids: number[], msg: unknown) {\n  for (const id of ids) {\n    if (id !== threadId) postMessageToThread(id, msg);\n  }\n}","typeGuard":"const isPeerThread = (id: number) => id !== threadId;","tryCatchPattern":"postMessageToThread(id, msg).catch((e) => {\n  if (e?.code !== 'ERR_WORKER_MESSAGING_SAME_THREAD') throw e;\n});","preventionTips":["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."],"tags":["worker-threads","messaging","thread-id"],"backgroundTag":"same-thread-messaging","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}