{"record":{"id":"79c395d52a3bf6e4","repo":"docusealco/docuseal","slug":"failed-to-load-document-from-file-file-path","errorCode":null,"errorMessage":"Failed to load document from file '#{file_path}', pointer is NULL.","messagePattern":"Failed to load document from file '#(.+?)', pointer is NULL\\.","errorType":"exception","errorClass":"Pdfium::PdfiumError","httpStatus":null,"severity":"error","filePath":"lib/pdfium.rb","lineNumber":606,"sourceCode":"\n      doc = new(doc_ptr)\n\n      return doc unless block_given?\n\n      begin\n        yield doc\n      ensure\n        doc.close\n      end\n    end\n\n    def self.open_file(file_path, password = nil)\n      doc_ptr = Pdfium.FPDF_LoadDocument(file_path, password)\n\n      if doc_ptr.null?\n        Pdfium.check_last_error(\"Failed to load document from file '#{file_path}'\")\n\n        raise PdfiumError, \"Failed to load document from file '#{file_path}', pointer is NULL.\"\n      end\n\n      doc = new(doc_ptr)\n\n      return doc unless block_given?\n\n      begin\n        yield doc\n      ensure\n        doc.close\n      end\n    end\n\n    def self.open_bytes(bytes, password = nil)\n      buffer = FFI::MemoryPointer.new(:char, bytes.bytesize)\n      buffer.put_bytes(0, bytes)\n\n      doc_ptr = Pdfium.FPDF_LoadMemDocument(buffer, bytes.bytesize, password)","sourceCodeStart":588,"sourceCodeEnd":624,"githubUrl":"https://github.com/docusealco/docuseal/blob/004a22c1c88109c7ba0b567df011a8cb13894001/lib/pdfium.rb#L588-L624","documentation":"Raised by Pdfium::Document.open_file when FPDF_LoadDocument returns a NULL document handle. Pdfium.check_last_error runs first and raises PdfiumError or PasswordError with the PDFium error code (FPDF_ERR_PASSWORD, FPDF_ERR_FORMAT, FPDF_ERR_FILE) when one is set, so this exact plain message means the load returned NULL with no recorded code. That is the classic signature of a missing/unreadable path or bytes that are not a PDF at all.","triggerScenarios":"open_file('/tmp/gone.pdf') after the tempfile was deleted; a path that points to a directory; a .docx or image renamed to .pdf; a zero-byte file. An encrypted PDF without a password raises the PasswordError variant from check_last_error instead of this message.","commonSituations":"Processing user uploads without validating content; stale paths from cleaned tmp directories; files still being written when load is attempted; double extensions or client-side MIME spoofing.","solutions":["Verify File.exist? and File.readable? before loading","Check the first bytes are '%PDF-' magic before calling open_file","If the path is fine but content is suspect, read the bytes and use Pdfium::Document.open_bytes so you control the input","Rescue Pdfium::PasswordError and retry with the password for encrypted files"],"exampleFix":"# before\ndoc = Pdfium::Document.open_file(upload_path)\n\n# after\nraise ArgumentError, 'not a PDF' unless File.file?(upload_path) && File.open(upload_path, 'rb') { |f| f.read(5) } == '%PDF-'\n\ndoc = Pdfium::Document.open_file(upload_path)","handlingStrategy":"validation","validationCode":"def loadable_pdf_file?(path)\n  File.file?(path) && File.readable?(path) && File.size(path).positive? &&\n    File.open(path, 'rb') { |f| f.read(5) == '%PDF-' }\nend\n\nraise ArgumentError, 'not a PDF' unless loadable_pdf_file?(path)","typeGuard":null,"tryCatchPattern":"begin\n  Pdfium::Document.open_file(path, password)\nrescue Pdfium::PasswordError\n  prompt_for_password_and_retry\nrescue Pdfium::PdfiumError => e\n  mark_upload_invalid(path, e.message)\nend","preventionTips":["Validate '%PDF-' magic bytes on every upload before any pdfium call","Rescue PasswordError separately from PdfiumError so encrypted files get a password flow instead of a generic failure","Never trust client-declared MIME types; check content"],"tags":["pdfium","file-loading","invalid-pdf","ffi","ruby"],"backgroundTag":"invalid-pdf-file","analyzedSha":"004a22c1c88109c7ba0b567df011a8cb13894001","analyzedAt":"2026-08-21T13:38:23.343Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}