{"record":{"id":"64fd49de07a85d2f","repo":"Stirling-Tools/Stirling-PDF","slug":"pdfium-failed-to-open-document-error-err","errorCode":null,"errorMessage":"PDFium: failed to open document (error ${err})","messagePattern":"PDFium: failed to open document \\(error (.+?)\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/core/services/pdfiumService.ts","lineNumber":311,"sourceCode":"/**\n * Load a PDF into PDFium memory and return the document pointer.\n * Caller MUST call `closeRawDocument(docPtr)` when finished.\n */\nexport async function openRawDocument(\n  data: ArrayBuffer | Uint8Array,\n  password?: string,\n): Promise<number> {\n  const m = await getPdfiumModule();\n  const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);\n  const len = bytes.length;\n  const ptr = m.pdfium.wasmExports.malloc(len);\n  copyToWasmHeap(m, bytes, ptr);\n\n  const docPtr = m.FPDF_LoadMemDocument(ptr, len, password ?? \"\");\n  if (!docPtr) {\n    m.pdfium.wasmExports.free(ptr);\n    const err = m.FPDF_GetLastError();\n    throw new Error(`PDFium: failed to open document (error ${err})`);\n  }\n  // Keep the buffer alive — freed in closeRawDocument()\n  _docDataPtrs.set(docPtr, ptr);\n  return docPtr;\n}\n\n/**\n * Open a raw document — convenience alias that delegates to {@link openRawDocument}.\n * Kept for API compatibility with callers that were updated to use the \"Safe\" variant.\n */\nexport async function openRawDocumentSafe(\n  data: ArrayBuffer | Uint8Array,\n  password?: string,\n): Promise<number> {\n  return openRawDocument(data, password);\n}\n\n/**","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/core/services/pdfiumService.ts#L293-L329","documentation":"`openRawDocument`/`openRawDocumentSafe` malloc a WASM buffer, copy bytes in, and call `FPDF_LoadMemDocument`. On failure (null pointer) it frees the buffer and reads `FPDF_GetLastError()`. PDFium error codes: 1=UNKNOWN, 2=FILE (read/access), 3=FORMAT (not a PDF or structurally invalid), 4=PASSWORD (wrong/missing password), 5=SECURITY (unsupported protection), 6=PAGE. This is the most diagnostic of the PDFium errors because it surfaces the numeric cause.","triggerScenarios":"Error 4: the PDF is user-password-protected and no/wrong password was supplied. Error 3: bytes are not a PDF, or the PDF is truncated/corrupt (bad xref/header). Error 2: data couldn't be read (rare for in-memory). Error 6: document opened but a page object is broken. Code 0/1: indeterminate.","commonSituations":"Exporting/rendering a password-protected PDF without prompting for the password; passing a `Uint8Array` whose underlying `ArrayBuffer` was detached; a partially-downloaded file; double-processing the same detached buffer after `transfer`.","solutions":["Map the error code to UX: 4 → password prompt flow; 3 → 'file is not a valid PDF or is damaged'; 5 → 'unsupported security'; 6 → 'a page in this document is corrupt'.","Before opening, verify the bytes start with `%PDF-` and the ArrayBuffer is not detached (`byteLength > 0`).","For password-protected files, obtain the password (see `isPDFUserPasswordProtected`) and pass it to `openRawDocumentSafe(data, password)`.","Never reuse a buffer that was transferred to a Worker (detached) — re-read from the File."],"exampleFix":"// before\nconst docPtr = m.FPDF_LoadMemDocument(ptr, len, password ?? \"\");\nif (!docPtr) {\n  m.pdfium.wasmExports.free(ptr);\n  const err = m.FPDF_GetLastError();\n  throw new Error(`PDFium: failed to open document (error ${err})`);\n}\n\n// after (human-readable codes)\nconst PDFIUM_ERRORS = { 1:\"unknown\",2:\"file access\",3:\"invalid/corrupt PDF\",4:\"password required\",5:\"unsupported security\",6:\"corrupt page\" };\nif (!docPtr) {\n  m.pdfium.wasmExports.free(ptr);\n  const err = m.FPDF_GetLastError();\n  throw new Error(`PDFium: failed to open document — ${PDFIUM_ERRORS[err] ?? `code ${err}`}`);\n}","handlingStrategy":"validation","validationCode":"// Cheap pre-checks before opening\nfunction looksLikePdfBytes(data: ArrayBuffer | Uint8Array): boolean {\n  const u = data instanceof Uint8Array ? data : new Uint8Array(data);\n  return u.length >= 5 && u[0] === 0x25 && u[1] === 0x50 && u[2] === 0x44 && u[3] === 0x46;\n}\nif (!looksLikePdfBytes(data)) throw new Error(\"Data is not a PDF\");\n\nconst PDFIUM_ERRORS: Record<number,string> = {1:\"unknown\",2:\"file access\",3:\"invalid/corrupt PDF\",4:\"password required\",5:\"unsupported security\",6:\"corrupt page\"};","typeGuard":"function isDetached(buf: ArrayBuffer | Uint8Array): boolean {\n  return (buf instanceof ArrayBuffer && buf.byteLength === 0 && (buf as any).__proto__ !== null) || false;\n}","tryCatchPattern":"try {\n  const docPtr = await openRawDocumentSafe(data, password);\n} catch (e) {\n  const m = e instanceof Error ? e.message : \"\";\n  const code = /error (\\d)/.exec(m)?.[1];\n  if (code === \"4\") promptForPassword();\n  else if (code === \"3\") notifyUser(\"This file is not a valid PDF or is damaged.\");\n  else throw e;\n}","preventionTips":["Verify the buffer starts with %PDF- and is non-empty/detached before opening.","Map PDFium error codes (4=password, 3=format, 5=security, 6=page) to distinct UX.","Obtain the password via the existing prompt flow for code-4 files.","Never reuse a buffer that was transferred to a Worker (detached) — re-read from the File."],"tags":["pdfium","wasm","document","password","corruption","validation"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}