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

  1. Send plain, structured-clone-safe data: primitives, plain objects/arrays, typed arrays, Map/Set, Date, ArrayBuffer views
  2. Replace callbacks with a request/response protocol — { id, type, payload } over postMessage, results posted back
  3. Transfer (don't copy) ArrayBuffers via the second argument: `worker.postMessage(data, [buf])`
  4. 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

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


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