mozilla/pdf.js · error · Error

Worker was destroyed

Error message

Worker was destroyed

What it means

A generic Error in getDocument's startup chain: after worker.promise resolves but before the GetDocRequest is sent, the worker was destroyed (worker.destroyed === true). The whole document load is aborted because there is no live worker to receive the request. This is a lifecycle error, not a data error.

Source

Thrown at src/display/api.js:407

      wasmUrl,
      hasGPU: false, // Set below.
    },
  };
  const transportParams = {
    ownerDocument,
    pdfBug,
    styleElement,
    enableHWA,
    loadingParams: {
      disableAutoFetch,
      enableXfa,
    },
  };

  Promise.all([worker.promise, gpuPromise])
    .then(function ([, hasGPU]) {
      if (worker.destroyed) {
        throw new Error("Worker was destroyed");
      }

      docParams.evaluatorOptions.hasGPU = hasGPU;

      const workerIdPromise = worker.messageHandler.sendWithPromise(
        "GetDocRequest",
        docParams,
        data ? [data.buffer] : null
      );

      let networkStream;
      if (data) {
        // The entire PDF was provided, no `networkStream` necessary.
      } else if (rangeTransport) {
        networkStream = new PDFDataTransportStream({
          pdfDataRangeTransport: rangeTransport,
          disableRange,
          disableStream,

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Track the loading task and only destroy it once you no longer need the document; avoid destroying during load.
  2. If cancelling is intentional, treat 'Worker was destroyed' as an expected rejection, not a bug.
  3. Ensure you await any prior task.destroy() before starting a new getDocument that reuses the worker/port.
  4. Use a separate PDFWorker per concurrent load instead of sharing one that gets torn down.

Example fix

// before
const task = pdfjsLib.getDocument({ url });
setTimeout(() => task.destroy(), 50); // kills worker mid-handshake

// after
let task = pdfjsLib.getDocument({ url });
try {
  const doc = await task.promise;
} catch (e) {
  if (/Worker was destroyed/.test(e.message)) return; // expected cancel
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Do not start a load while a destroy is pending on the shared worker.
if (worker.destroyed) {
  throw new Error('Refusing to load: worker already destroyed');
}

Try / catch

try {
  const doc = await loadingTask.promise;
} catch (e) {
  if (/Worker was destroyed/.test(e.message)) return null; // expected cancel
  throw e;
}

Prevention

When it happens

Trigger: Calling loadingTask.destroy() or worker.destroy() during the brief window after worker setup but before GetDocRequest is dispatched; destroying a previously-started loading task and immediately starting a new one reusing the same worker; rapid mount/unmount in a SPA.

Common situations: React/Vue components that tear down before the async load resolves; navigation away from a page mid-load; cancelling a queued load after a timeout.

Related errors


AI-assisted analysis of mozilla/pdf.js@5903d58d58 (2026-08-13). Data as JSON: /api/errors/b69f847236626af9. Report an issue: GitHub.