Stirling-Tools/Stirling-PDF · error · Error

Failed to export PDF

Error message

Failed to export PDF

What it means

Thrown in EmbedPdfViewer's export workflow when exportActions.saveAsCopy() returns a falsy value (null, undefined, or empty). saveAsCopy() is an EmbedPDF SDK method that serializes the currently loaded document (with annotations and redactions) into an ArrayBuffer. A null return means the SDK could not produce a valid PDF byte stream.

Source

Thrown at frontend/editor/src/core/components/viewer/EmbedPdfViewer.tsx:629

        (redactionTrackerRef.current?.getPendingCount() ?? 0) > 0;

      // Mark redactions as applied BEFORE committing, so the button stays enabled during the save process
      // This ensures the button doesn't become disabled when pendingCount becomes 0
      if (hadPendingRedactions || redactionsApplied) {
        setRedactionsApplied(true);
      }

      if (hadPendingRedactions) {
        console.log("[Viewer] Committing pending redactions before export");
        redactionTrackerRef.current?.commitAllPending();
        // Give a small delay for the commit to process
        await new Promise((resolve) => setTimeout(resolve, 100));
      }

      // Step 1: Export PDF with annotations using EmbedPDF
      const arrayBuffer = await exportActions.saveAsCopy();
      if (!arrayBuffer) {
        throw new Error("Failed to export PDF");
      }

      // Step 2: Convert ArrayBuffer to File
      const blob = new Blob([arrayBuffer], { type: "application/pdf" });
      const filename = currentFile.name || "document.pdf";
      const file = new File([blob], filename, { type: "application/pdf" });

      // Step 3: Create StirlingFiles and stubs for version history
      // Only consume the current file, not all active files
      const currentFileId = currentFileStableId;
      if (!currentFileId) throw new Error("Current file ID not found");

      const parentStub = selectors.getStirlingFileStub(currentFileId);
      if (!parentStub) throw new Error("Parent stub not found");

      const { stirlingFiles, stubs } = await createStirlingFilesAndStubs(
        [file],
        parentStub,

View on GitHub (pinned to 9ef20dcab8)

Solutions

  1. Disable the export button until EmbedPDF signals the document is fully loaded (track a ready state).
  2. Check arrayBuffer.byteLength > 0 in addition to truthiness — an empty buffer is also invalid.
  3. Retry saveAsCopy() once after a short delay, as transient EmbedPDF state issues can self-resolve.
  4. Log exportActions state (session validity, document loaded flag) when the null is hit to diagnose the SDK's internal cause.

Example fix

// before
const arrayBuffer = await exportActions.saveAsCopy();
if (!arrayBuffer) {
  throw new Error("Failed to export PDF");
}

// after
const arrayBuffer = await exportActions.saveAsCopy();
if (!arrayBuffer || arrayBuffer.byteLength === 0) {
  throw new Error(
    "EmbedPDF could not export the document. Ensure the document is fully loaded and try again."
  );
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Check EmbedPDF document ready state before export
if (!embedPdfReady || !exportActions) {
  showAlert('Document is still loading. Please wait before exporting.');
  return;
}
// Validate the export action exists
if (typeof exportActions.saveAsCopy !== 'function') {
  throw new Error('EmbedPDF export is not available in this session.');
}

Type guard

function isValidPdfBuffer(data: unknown): data is ArrayBuffer {
  return data instanceof ArrayBuffer && data.byteLength > 0;
}

Try / catch

try {
  const arrayBuffer = await exportActions.saveAsCopy();
  if (!arrayBuffer || arrayBuffer.byteLength === 0) {
    // Retry once after short delay
    await new Promise(r => setTimeout(r, 200));
    const retry = await exportActions.saveAsCopy();
    if (!retry) throw new Error('EmbedPDF export failed after retry.');
    return retry;
  }
} catch (error) {
  setErrorMessage(`PDF export failed: ${error instanceof Error ? error.message : 'unknown error'}`);
}

Prevention

When it happens

Trigger: The EmbedPDF viewer has not finished loading the document, the document is in a corrupted state after redaction commits, the EmbedPDF session/license expired, or the SDK encountered an internal serialization error that it reported via null rather than throwing.

Common situations: User clicks export immediately after loading a large PDF before EmbedPDF finishes initialization. Pending redactions were committed (commitAllPending) but the internal state is inconsistent. EmbedPDF evaluation/session token expired mid-session.

Related errors


AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13). Data as JSON: /api/errors/20527e761b7fdf6c. Report an issue: GitHub.