denoland/deno · error · DOMException

DataCloneError

DataCloneError

Error message

${err}

What it means

Inside a dedicated worker, the global postMessage (DedicatedWorkerGlobalScope, runtime/js/99_main.js) serializes with the same structured-clone machinery as the host side. The fast path (no transfer argument) runs serializeMessageData and wraps serialization failures as a DOMException named DataCloneError with the serializer's message. Non-cloneable values — functions above all — are rejected before anything reaches the parent.

Source

Thrown at runtime/js/99_main.js:252

  }

  isClosing = true;
  op_worker_close();
}

function postMessage(message, transferOrOptions = { __proto__: null }) {
  const prefix =
    "Failed to execute 'postMessage' on 'DedicatedWorkerGlobalScope'";
  webidl.requiredArguments(arguments.length, 1, prefix);
  // Fast path: no transferables
  if (
    transferOrOptions === undefined ||
    transferOrOptions === null ||
    (arguments.length <= 1)
  ) {
    op_worker_post_message_raw(
      messagePort.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 f7822238ca)

Solutions

  1. Post plain, structured-clone-safe result data (primitives, plain objects, typed arrays, Map/Set, Date)
  2. Pre-validate locally with `structuredClone(message)` — it applies the same rules and fails at the true call site
  3. Use the transfer array for ArrayBuffers: `self.postMessage(data, [buf])`
  4. Keep functions in the worker; send an id the parent can correlate

Example fix

// before (inside the worker)
self.postMessage({ handler: onDone, value });

// after
self.postMessage({ type: 'done', value });
Defensive patterns

Strategy: try-catch

Validate before calling

structuredClone(message); // throws DataCloneError locally if the payload is not cloneable
self.postMessage(message);

Try / catch

try {
  self.postMessage(result);
} catch (e) {
  if (e instanceof DOMException && e.name === 'DataCloneError') {
    self.postMessage({ type: 'error', message: 'result not cloneable' });
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: `self.postMessage({ handler: onDone })` inside the worker; posting class instances with own function properties or live native handles; sending a message containing a promise or symbol-keyed behavior object.

Common situations: Worker result objects that accidentally include callbacks or class instances; sharing request-scoped objects back to the parent; code moved from a same-thread context where references were passed around.

Related errors


AI-assisted analysis of denoland/deno@f7822238ca (2026-08-20). Data as JSON: /api/errors/279a207c23acd53b. Report an issue: GitHub.