Skip to content

node:worker_threads.markAsUncloneable is missing (typeof === "undefined"); breaks undici 8.0.3+ (CacheStorage / Response / Request / WebSocket / FormData / Headers / EventSource) #29423

@Arv1nt3

Description

@Arv1nt3

What version of Bun is running?

1.3.12+700fc117a

What platform is your computer?

Linux 6.6.87.2-microsoft-standard-WSL2 x86_64 x86_64

What steps can reproduce the bug?

Bun does not export markAsUncloneable from node:worker_threads, even though the Bun reference docs claim the API is supported (the docs appear to be auto-generated from Node's type declarations).

Minimal repro — the API itself is missing:

$ bun -e 'console.log(typeof require("node:worker_threads").markAsUncloneable)'
undefined

Real-world repro via undici 8.1.0, which is where this actually bites users. undici 8.0.3 removed its runtime feature probe for markAsUncloneable (nodejs/undici#4968) and 8.1.0's lib/web/webidl/index.js now calls require("node:worker_threads").markAsUncloneable unconditionally. Any undici constructor that goes through webidl.util.markAsUncloneable(this)CacheStorage, Response, Request, Headers, FormData, WebSocket, EventSource — throws at module-load time:

mkdir /tmp/bun-uncloneable-repro && cd /tmp/bun-uncloneable-repro
bun init -y
bun add undici@^8.1.0
bun -e 'require("undici/lib/web/cache/cachestorage.js"); new (require("undici")).CacheStorage'

Actual output:

TypeError: webidl.util.markAsUncloneable is not a function

Spec: https://nodejs.org/api/worker_threads.html#workermarkasuncloneableobject

What is the expected behavior?

Matching Node.js ≥ v21:

  • typeof require("node:worker_threads").markAsUncloneable === "function".
  • markAsUncloneable(obj) returns undefined. It is a no-op for primitives (including null / undefined) and for ArrayBuffer / SharedArrayBuffer / Buffer-like objects (per the Node docs).
  • For any other object, subsequent attempts to clone it via structuredClone(obj), MessagePort.postMessage(obj), new Worker(..., { workerData: obj }), or BroadcastChannel.postMessage(obj) throw a DOMException with name === "DataCloneError".
  • The marking is irreversible.
  • The object stays usable locally (property access, JSON.stringify, etc. work normally).
  • undici 8.0.3+ loads and instantiates CacheStorage / Response / Request / Headers / FormData / WebSocket / EventSource without throwing.

Node reference:

$ node -e 'console.log(typeof require("node:worker_threads").markAsUncloneable)'
function

$ node -e '
  const { markAsUncloneable } = require("node:worker_threads");
  const obj = { a: 1 };
  markAsUncloneable(obj);
  try { structuredClone(obj); } catch (e) { console.log(e.name, "-", e.message); }
'
DataCloneError - [object Object] could not be cloned.

What do you see instead?

$ bun -e 'console.log(typeof require("node:worker_threads").markAsUncloneable)'
undefined

$ bun -e 'require("undici/lib/web/cache/cachestorage.js"); new (require("undici")).CacheStorage'
error: webidl.util.markAsUncloneable is not a function
      at <anonymous> (.../node_modules/undici/lib/web/webidl/index.js:…)

Every undici 8.0.3+ surface that passes this through webidl.util.markAsUncloneable fails to construct: CacheStorage, Response, Request, Headers, FormData, WebSocket, EventSource. This cascades into any Next.js / Vitest / jsdom / cheerio app running on Bun.

Additional information

  • The Bun reference docs list markAsUncloneable as supported (https://bun.com/reference/node/worker_threads/markAsUncloneable) but the runtime does not export it. The docs look auto-generated from Node's .d.ts.
  • Bun already implements the sibling stub worker_threads.markAsUntransferable (currently a throwNotImplemented stub).
  • Node.js added markAsUncloneable in v21.0.0. Spec: https://nodejs.org/api/worker_threads.html#workermarkasuncloneableobject.
  • I have a PR ready that implements this and will reference this issue: tagging with a JSC private-name identifier and hooking CloneSerializer::dumpIfTerminal in SerializedScriptValue.cpp so structuredClone, MessagePort.postMessage, Worker workerData, and BroadcastChannel.postMessage all throw DataCloneError for marked objects. ArrayBuffer / SharedArrayBuffer / Buffer-like values remain a no-op per spec.
  • Related: tracked stability issues in worker_threads (Worker & worker_threads stability tracking issue #15964) — this fix is intentionally scoped to just markAsUncloneable and does not touch those.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions