| name | pdflib-errors-loading |
| description | Use when PDFDocument.load() fails with encryption errors, malformed PDF errors, or unexpected parsing failures. Prevents silent data loss: pdf-lib cannot decrypt PDFs — ignoreEncryption only skips the check, it does not decrypt content. Covers encrypted PDF handling, malformed PDF recovery, load options, error patterns. Keywords: load error, encrypted, ignoreEncryption, malformed, throwOnInvalidObject, parse error, can't open PDF, PDF won't load, encrypted PDF error, corrupted PDF, PDF parse failed.
|
| license | MIT |
| compatibility | Designed for Claude Code. Requires pdf-lib 1.x with TypeScript/JavaScript. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
pdflib-errors-loading
Diagnoses and fixes all errors related to PDFDocument.load() in pdf-lib 1.x. Covers encrypted PDFs, malformed documents, load option misconfiguration, and metadata side effects.
Quick Reference
const pdfDoc = await PDFDocument.load(pdfBytes);
const pdfDoc = await PDFDocument.load(pdfBytes, { ignoreEncryption: true });
const pdfDoc = await PDFDocument.load(pdfBytes, { throwOnInvalidObject: true });
const pdfDoc = await PDFDocument.load(pdfBytes, { updateMetadata: false });
const pdfDoc = await PDFDocument.load(pdfBytes, { capNumbers: true });
const isEnc: boolean = pdfDoc.isEncrypted;
LoadOptions Reference
| Option | Type | Default | Purpose |
|---|
ignoreEncryption | boolean | false | Skip encryption validation on load |
throwOnInvalidObject | boolean | false | Throw on malformed PDF objects instead of silently skipping |
updateMetadata | boolean | true | Auto-set producer, creator, creation/modification dates |
capNumbers | boolean | false | Limit numeric precision to avoid floating-point issues |
parseSpeed | number | — | Control parsing performance level |
Decision Tree: Loading Error Diagnosis
PDFDocument.load() throws an error
|
+-- Error mentions "encrypted" or "encryption"
| +-- PDF has owner password (print/copy restrictions)?
| | +-- Use { ignoreEncryption: true }
| | +-- This bypasses the restriction CHECK, not the encryption itself
| | +-- Content is accessible because owner-password PDFs store content unencrypted
| |
| +-- PDF has user password (content is actually encrypted)?
| +-- pdf-lib CANNOT decrypt this PDF
| +-- NEVER use ignoreEncryption hoping it will decrypt content
| +-- Pre-process with external tool: qpdf, mutool, or pdftk
| +-- Then load the decrypted output with pdf-lib
|
+-- Error mentions "invalid object" or parsing failure
| +-- PDF is malformed or corrupted?
| | +-- Try { throwOnInvalidObject: false } (default) to skip bad objects
| | +-- If that works: document loads but may have missing content
| | +-- If that fails: PDF is too corrupted for pdf-lib
| |
| +-- Need strict validation?
| +-- Use { throwOnInvalidObject: true }
| +-- Catch the error to identify which objects are invalid
|
+-- Error mentions unexpected token or format
| +-- Input is not valid PDF bytes?
| | +-- Verify input is Uint8Array, ArrayBuffer, or base64 string
| | +-- Verify the file starts with %PDF header
| | +-- NEVER pass a file path string — pdf-lib does not read files
| |
| +-- Input is a file path or URL?
| +-- Read the file first: fs.readFileSync() or fetch().arrayBuffer()
| +-- Then pass the resulting bytes to PDFDocument.load()
|
+-- Metadata unexpectedly changed after load?
+-- Default behavior: updateMetadata is true
+-- Use { updateMetadata: false } to preserve original metadata
+-- See "updateMetadata Side Effects" section below
Error: Encrypted PDF
Cause
pdf-lib throws when loading a PDF that contains encryption dictionary markers. This happens with BOTH owner-password (restriction-only) and user-password (content-encrypted) PDFs.
The Critical Distinction
Owner-password PDFs (restrictions like no-print, no-copy):
- Content is stored UNENCRYPTED in the file
- The encryption dictionary only signals viewer restrictions
ignoreEncryption: true works correctly — it skips the restriction check
- You can read, modify, and save the document normally
User-password PDFs (content is actually encrypted):
- Content bytes are encrypted and unreadable without the password
ignoreEncryption: true skips the encryption CHECK but does NOT decrypt content
- Loading succeeds but content operations produce garbage or errors
- pdf-lib has NO decryption engine and NO password support
Fix for Owner-Password PDFs
import { PDFDocument } from 'pdf-lib';
const pdfBytes = fs.readFileSync('restricted.pdf');
const pdfDoc = await PDFDocument.load(pdfBytes, {
ignoreEncryption: true,
});
const pages = pdfDoc.getPages();
console.log(`Loaded ${pages.length} pages`);
if (pdfDoc.isEncrypted) {
console.log('Document has encryption markers');
}
Fix for User-Password PDFs
import { PDFDocument } from 'pdf-lib';
const decryptedBytes = fs.readFileSync('decrypted.pdf');
const pdfDoc = await PDFDocument.load(decryptedBytes);
NEVER Do This
const pdfDoc = await PDFDocument.load(encryptedBytes, {
ignoreEncryption: true,
});
Error: Malformed PDF Objects
Cause
The PDF file contains objects that do not conform to PDF specification. This happens with PDFs generated by buggy software, corrupted during transfer, or hand-edited.
Fix: Lenient Mode (Default)
const pdfDoc = await PDFDocument.load(corruptedBytes);
Fix: Strict Mode for Debugging
try {
const pdfDoc = await PDFDocument.load(corruptedBytes, {
throwOnInvalidObject: true,
});
} catch (error) {
console.error('Invalid PDF object found:', error.message);
}
updateMetadata Side Effects
The Problem
By default, PDFDocument.load() sets updateMetadata: true. This AUTOMATICALLY overwrites four metadata fields:
| Field | Overwritten Value |
|---|
producer | "pdf-lib (https://github.com/Hopding/pdf-lib)" |
creator | "pdf-lib (https://github.com/Hopding/pdf-lib)" |
creationDate | Current timestamp |
modificationDate | Current timestamp |
When This Causes Problems
- Archival workflows that must preserve original metadata
- Legal documents where creation date must remain unchanged
- Document audit trails that track producer software
- Round-trip editing where metadata must stay intact
Fix: Preserve Original Metadata
const pdfDoc = await PDFDocument.load(pdfBytes, {
updateMetadata: false,
});
Fix: Selective Metadata Control
const pdfDoc = await PDFDocument.load(pdfBytes, {
updateMetadata: false,
});
pdfDoc.setModificationDate(new Date());
Restricted Document Handling for Form Filling
The Problem
Some PDFs have owner-password restrictions that specifically prohibit form filling. pdf-lib throws an encryption error when loading these documents, even though the form data itself is not encrypted.
The Fix
import { PDFDocument } from 'pdf-lib';
const pdfDoc = await PDFDocument.load(restrictedFormBytes, {
ignoreEncryption: true,
});
const form = pdfDoc.getForm();
form.getTextField('name').setText('John Doe');
const pdfBytes = await pdfDoc.save();
CRITICAL: This works because owner-password restrictions are viewer-level enforcements, not content-level encryption. The form field data is stored unencrypted.
capNumbers Option
When to Use
Use capNumbers: true when loading PDFs that contain extremely large or precise floating-point numbers that cause JavaScript numeric overflow or precision issues.
const pdfDoc = await PDFDocument.load(pdfBytes, {
capNumbers: true,
});
Symptoms That Indicate You Need capNumbers
NaN or Infinity values appearing in page dimensions
- Rendering artifacts from extreme coordinate values
- JavaScript numeric overflow errors during page operations
parseSpeed Option
Purpose
Controls the parsing performance level during document loading. Higher values may skip certain validation steps for faster loading of large documents.
const pdfDoc = await PDFDocument.load(largePdfBytes, {
parseSpeed: 150,
});
isEncrypted Property
const pdfDoc = await PDFDocument.load(pdfBytes, {
ignoreEncryption: true,
});
if (pdfDoc.isEncrypted) {
}
ALWAYS check isEncrypted after loading with ignoreEncryption: true to confirm the document's encryption status. Use this as a diagnostic flag, not a guarantee that content is accessible.
Input Validation Errors
Valid Input Types for PDFDocument.load()
| Type | Example |
|---|
Uint8Array | fs.readFileSync('file.pdf') in Node.js |
ArrayBuffer | await fetch(url).then(r => r.arrayBuffer()) |
string (base64) | Base64-encoded PDF content |
string (data URI) | "data:application/pdf;base64,..." |
NEVER Pass These
await PDFDocument.load('/path/to/file.pdf');
await PDFDocument.load('https://example.com/file.pdf');
await PDFDocument.load('Hello World');
const bytes = fs.readFileSync('/path/to/file.pdf');
await PDFDocument.load(bytes);
Combined Options Pattern
For maximum compatibility when loading unknown PDFs:
const pdfDoc = await PDFDocument.load(unknownPdfBytes, {
ignoreEncryption: true,
throwOnInvalidObject: false,
updateMetadata: false,
capNumbers: true,
});
ALWAYS start with the most lenient options when debugging loading failures, then tighten options once the root cause is identified.
Reference Files