Stirling-Tools/Stirling-PDF · error · Error

Failed to export PDF: ${error instanceof Error ? error.messa

Error message

Failed to export PDF: ${error instanceof Error ? error.message : "Unknown error"}

What it means

This is the catch-all wrapper at the end of `exportPDF`. Every error thrown inside the try (the 'No pages to export' precondition, `file.arrayBuffer()` failure, or a `createSingleDocument`/PDFium failure) is re-wrapped as `Failed to export PDF: <inner.message>` with the original attached via `{cause}`. The wrapper itself is not a distinct failure mode — it obscures the real error behind a generic prefix.

Source

Thrown at frontend/editor/src/core/services/pdfExportService.ts:58

          : pdfDocument.pages;

      if (pagesToExport.length === 0) {
        throw new Error("No pages to export");
      }

      const originalPDFBytes = await pdfDocument.file.arrayBuffer();
      const blob = await this.createSingleDocument(
        originalPDFBytes,
        pagesToExport,
      );
      const exportFilename = this.generateFilename(
        filename || pdfDocument.name,
      );

      return { blob, filename: exportFilename };
    } catch (error) {
      console.error("PDF export error:", error);
      throw new Error(
        `Failed to export PDF: ${error instanceof Error ? error.message : "Unknown error"}`,
        { cause: error },
      );
    }
  }

  /**
   * Export PDF document with applied operations (multi-file source)
   */
  async exportPDFMultiFile(
    pdfDocument: PDFDocument,
    sourceFiles: Map<string, File>,
    selectedPageIds: string[] = [],
    options: ExportOptions = {},
  ): Promise<{ blob: Blob; filename: string }> {
    const { selectedOnly = false, filename } = options;

    try {

View on GitHub (pinned to 9ef20dcab8)

Solutions

  1. When catching, inspect `error.cause` and branch on known inner errors ('No pages to export' → validation UX; PDFium errors → retry/reload-WASM UX) instead of showing the generic prefix.
  2. Let known precondition errors (validation) propagate unwrapped so callers can distinguish them from engine failures.
  3. Telemetry: log `error.cause?.message` alongside the wrapper so the real cause is captured.
  4. Ensure `pdfDocument.file` is still a live Blob (not revoked) before export.

Example fix

// before
} catch (error) {
  console.error("PDF export error:", error);
  throw new Error(`Failed to export PDF: ${...}`, { cause: error });
}

// after (preserve known errors verbatim, wrap only unknowns)
} catch (error) {
  if (error instanceof Error && error.message === "No pages to export") throw error;
  console.error("PDF export error:", error);
  throw new Error(`Failed to export PDF: ${...}`, { cause: error });
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Confirm the source blob is still live before exporting
if (!pdfDocument.file || pdfDocument.file.size === 0) {
  notifyUser("The source file is no longer available.");
  return;
}

Type guard

function hasCause(e: unknown): e is Error & { cause: unknown } {
  return e instanceof Error && "cause" in e;
}

Try / catch

try {
  return await exportService.exportPDF(doc, ids, opts);
} catch (e) {
  const cause = (e as Error & { cause?: Error }).cause;
  const msg = cause instanceof Error ? cause.message : "";
  if (msg === "No pages to export") notifyUser("Nothing to export.");
  else if (msg.startsWith("PDFium")) notifyUser("PDF engine error. Please retry.");
  else throw e;
}

Prevention

When it happens

Trigger: Any failure during single-file export: empty page selection (error 24), `pdfDocument.file.arrayBuffer()` rejecting (blob revoked/lost), PDFium `FPDF_CreateNewDocument` returning null (error 29), or an `importPages` failure.

Common situations: User clicks Export and something earlier threw; the cause chain holds the real reason but only the prefixed message is shown in the UI.

Related errors


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