denoland/deno · error · TypeError

Invalid state: File-backed Blobs are not cloneable

Error message

Invalid state: File-backed Blobs are not cloneable

What it means

TypeError thrown by cloneBlobParts, the serialization hook used when a Blob (or File) is structured-cloned — via structuredClone, postMessage, or the host-object serialization path (ext/web/09_file.js). Blobs marked file-backed — created by markFileBackedBlob, which in Deno is applied to Blobs returned from node:fs openAsBlob (ext/node/polyfills/fs.ts) — are intentionally rejected by the clone serializer, matching Node.js behavior, because their backing storage is tied to the file handle's lifetime.

Source

Thrown at ext/web/09_file.js:735

  for (let i = 0; i < parts.length; ++i) {
    const part = parts[i];
    if (ObjectPrototypeIsPrototypeOf(BlobPrototype, part)) {
      getPartRefs(part, bag);
    } else {
      ArrayPrototypePush(bag, part);
    }
  }
  return bag;
}

/**
 * Clone blob part references in BlobStore and return serializable metadata.
 * @param {Blob} blob
 * @returns {{ uuid: string, size: number }[]}
 */
function cloneBlobParts(blob) {
  if (blob[_fileBacked]) {
    throw new TypeError("Invalid state: File-backed Blobs are not cloneable");
  }
  const refs = getPartRefs(blob);
  const cloned = [];
  for (let i = 0; i < refs.length; ++i) {
    ArrayPrototypePush(cloned, op_blob_clone_part(refs[i]._id));
  }
  return cloned;
}

ObjectDefineProperty(Blob.prototype, core.hostObjectBrand, {
  __proto__: null,
  value: function () {
    return {
      type: "Blob",
      mimeType: this[_type],
      parts: cloneBlobParts(this),
      size: this[_size],
    };

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Materialize into a plain Blob before posting: const plain = new Blob([await blob.arrayBuffer()], { type: blob.type }).
  2. Send the bytes instead of the Blob: postMessage(await blob.bytes()) or transfer an ArrayBuffer.
  3. If only the file path matters, post the path string and re-open the blob in the receiving realm.

Example fix

// before
import { openAsBlob } from 'node:fs';
const blob = await openAsBlob('./big.bin');
worker.postMessage(blob); // TypeError: file-backed Blob not cloneable

// after
import { openAsBlob } from 'node:fs';
const blob = await openAsBlob('./big.bin');
const plain = new Blob([await blob.arrayBuffer()], { type: blob.type });
worker.postMessage(plain); // clones fine
Defensive patterns

Strategy: fallback

Validate before calling

// File-backed provenance is not observable from JS (internal symbol),
// so convert anything that might be file-backed into a plain Blob first.
async function toCloneableBlob(maybeFileBacked) {
  if (!maybeFileBacked) return new Blob();
  return new Blob([await maybeFileBacked.arrayBuffer()], {
    type: maybeFileBacked.type,
  });
}
worker.postMessage(await toCloneableBlob(blob));

Try / catch

try {
  worker.postMessage(blob);
} catch (err) {
  if (err instanceof TypeError && /File-backed Blobs are not cloneable/.test(err.message)) {
    const plain = new Blob([await blob.arrayBuffer()], { type: blob.type });
    worker.postMessage(plain);
  } else throw err;
}

Prevention

When it happens

Trigger: structuredClone(blob) or worker.postMessage(blob) where blob came from fs.openAsBlob()/fsPromises.openAsBlob(path); building a File from such a blob's parts and posting it; passing it through a MessageChannel.

Common situations: Node-compat code reading large files as Blobs for zero-copy-ish handling and then sending them to a Worker; libraries (e.g. file-upload helpers) that forward fs.openAsBlob results across realms; version changes where clone support for these blobs was removed to match Node.

Related errors


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