denoland/deno · error · DOMException
DataCloneError
DataCloneError
Error message
${err} What it means
Worker.postMessage serializes the message with structured clone before handing it to the host. The fast path (no transfer argument) calls serializeMessageData, whose error callback wraps any failure as a DOMException named DataCloneError; the slow path's serializeJsMessageData fails the same way. Values that cannot be cloned include functions, objects with own function properties, and native handles lacking a registered serializer.
Source
Thrown at runtime/js/11_workers.js:329
if (!this.#dispatchWorkerMessage(syncData)) return;
}
}
};
postMessage(message, transferOrOptions = { __proto__: null }) {
const prefix = "Failed to execute 'postMessage' on 'MessagePort'";
webidl.requiredArguments(arguments.length, 1, prefix);
if (this.#status !== "RUNNING") return;
// Fast path: no transferables
if (
transferOrOptions === undefined ||
transferOrOptions === null ||
(arguments.length <= 1)
) {
op_host_post_message_raw(
this.#id,
serializeMessageData(message, (err) => {
throw new DOMException(err, "DataCloneError");
}),
);
return;
}
message = webidl.converters.any(message);
let options;
if (
webidl.type(transferOrOptions) === "Object" &&
transferOrOptions !== undefined &&
transferOrOptions[SymbolIterator] !== undefined
) {
const transfer = webidl.converters["sequence<object>"](
transferOrOptions,
prefix,
"Argument 2",
);
options = { transfer };
} else {View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Send plain, structured-clone-safe data: primitives, plain objects/arrays, typed arrays, Map/Set, Date, ArrayBuffer views
- Replace callbacks with a request/response protocol — { id, type, payload } over postMessage, results posted back
- Transfer (don't copy) ArrayBuffers via the second argument: `worker.postMessage(data, [buf])`
- If the worker needs code, import it there, or send source text and evaluate it deliberately
Example fix
// before
worker.postMessage({ run: () => heavy(x) }); // DataCloneError
// after
worker.postMessage({ id: crypto.randomUUID(), type: 'run', payload: x });
// worker side implements the behavior and posts { id, result } back Defensive patterns
Strategy: try-catch
Validate before calling
function hasNonCloneable(value, seen = new WeakSet()) {
if (value === null || typeof value !== 'object') return typeof value === 'function';
if (seen.has(value)) return false;
seen.add(value);
return Object.values(value).some((v) => hasNonCloneable(v, seen));
}
if (hasNonCloneable(message)) {
throw new Error('refusing to postMessage: message contains functions');
}
worker.postMessage(message); Try / catch
try {
worker.postMessage(msg);
} catch (e) {
if (e instanceof DOMException && e.name === 'DataCloneError') {
worker.postMessage(JSON.parse(JSON.stringify(msg))); // JSON round-trip drops functions
} else {
throw e;
}
} Prevention
- Restrict worker messages to JSON-safe shapes; define a message type union and construct only those
- Validate locally with structuredClone(msg) first — same rules, immediate error at the true call site
- Use the transfer array for ArrayBuffers instead of copying large buffers
- Design worker RPC as { id, type, payload } messages; never attempt to send behavior
When it happens
Trigger: `worker.postMessage({ run: () => {} })`; sending promises, bound methods, or class instances carrying own function fields; sending resources (sockets, handles) that Deno has no cross-isolate serializer for; passing a second argument that is neither a transfer array nor options.
Common situations: Trying to ship behavior (callbacks) to a worker instead of a message protocol; porting in-process code that shared objects by reference; sending live client objects across isolates.
Related errors
- DataCloneError
- DataCloneError
- DataCloneError
- DataCloneError
- Unsupported KeyObject type for structured clone: ${data.keyT
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/98954e291846e837.
Report an issue: GitHub.