mozilla/pdf.js · error · Error

startCleanup: Page ${page.pageNumber} is currently rendering

Error message

startCleanup: Page ${page.pageNumber} is currently rendering.

What it means

Thrown by PDFDocumentProxy.startCleanup() when at least one cached PDFPageProxy reports cleanup() === false, meaning it is still rendering (its rendering state is not INITIAL). startCleanup wants every page idle so it can safely release common objects, fonts and method promises; an in-flight page would be corrupted by the teardown that follows.

Source

Thrown at src/display/api.js:3221

  getMarkInfo() {
    return this.messageHandler.sendWithPromise("GetMarkInfo", null);
  }

  getRawData(data) {
    return this.messageHandler.sendWithPromise("GetRawData", data);
  }

  async startCleanup(keepLoadedFonts = false) {
    if (this.destroyed) {
      return; // No need to manually clean-up when destruction has started.
    }
    await this.messageHandler.sendWithPromise("Cleanup", null);

    for (const page of this.#pageCache.values()) {
      const cleanupSuccessful = page.cleanup();

      if (!cleanupSuccessful) {
        throw new Error(
          `startCleanup: Page ${page.pageNumber} is currently rendering.`
        );
      }
    }
    this.commonObjs.clear();
    if (!keepLoadedFonts) {
      this.fontLoader.clear();
    }
    this.#methodPromises.clear();
    this.filterFactory.destroy(/* keepHCM = */ true);
    TextLayer.cleanup();
  }

  cachedPageNumber(ref) {
    if (!isRefProxy(ref)) {
      return null;
    }
    const refStr = ref.gen === 0 ? `${ref.num}R` : `${ref.num}R${ref.gen}`;

View on GitHub (pinned to 5903d58d58)

Solutions

  1. Await (or cancel and await) every renderTask.promise before calling startCleanup().
  2. Use renderTask.cancel() then await renderTask.promise.catch(() => {}) to force pages back to INITIAL.
  3. If cleanup is opportunistic, wrap startCleanup() in try/catch and retry once renders settle.

Example fix

// before
await page.render({ canvasContext, viewport });
await pdfDocument.startCleanup();

// after
const task = page.render({ canvasContext, viewport });
await task.promise;
await pdfDocument.startCleanup();

// or, when tearing down mid-render:
task.cancel();
await task.promise.catch(() => {});
await pdfDocument.startCleanup();
Defensive patterns

Strategy: validation

Validate before calling

// ensure every cached page is idle before cleanup
const busy = [...pdf._transport._pageCache.values?.() ?? []];
// simpler: track your own render tasks and await them
await Promise.allSettled(activeRenderTasks.map(t => t.promise));
await pdf.startCleanup();

Type guard

null

Try / catch

try {
  await pdf.startCleanup();
} catch (e) {
  if (e.message.startsWith('startCleanup:')) { /* retry after renders settle */ }
  else throw e;
}

Prevention

When it happens

Trigger: Calling startCleanup() while a page.render() task is still running; calling it right after render() without awaiting the returned promise; cancelled-but-not-yet-settled render tasks.

Common situations: Viewer teardown during scroll/zoom where the previous render is mid-flight; aggressive memory management triggered by a low-memory event before the render promise resolved; tests that forget to await renderTask.promise.

Related errors


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