docusealco/docuseal · error · Pdfium::PdfiumError
Failed to load document from IO, pointer is NULL.
Error message
Failed to load document from IO, pointer is NULL.
What it means
Raised by Pdfium::Document.open_io when FPDF_LoadCustomDocument returns NULL. open_io first delegates to open_file when the IO responds to path (after flush); otherwise it installs an FPDF_FILEACCESS read callback backed by io.seek/io.read and declares io.size as m_FileLen. This failure means the callback-based reads came up short (the callback returns 0 on short reads), the declared length was wrong, or the stream is corrupt or encrypted. check_last_error runs first and raises the error-code variant (PasswordError for FPDF_ERR_PASSWORD) when a code was recorded.
Source
Thrown at lib/pdfium.rb:674
bytes = io.read(size).to_s
out.put_bytes(0, bytes)
bytes.bytesize == size ? 1 : 0
end
file_access = Pdfium::FPDF_FILEACCESS.new
file_access[:m_FileLen] = io.size
file_access[:m_GetBlock] = get_block
file_access[:m_Param] = FFI::Pointer::NULL
doc_ptr = Pdfium.FPDF_LoadCustomDocument(file_access, password)
if doc_ptr.null?
Pdfium.check_last_error('Failed to load document from IO')
raise PdfiumError, 'Failed to load document from IO, pointer is NULL.'
end
doc = new(doc_ptr, [file_access, get_block, io])
return doc unless block_given?
begin
yield doc
ensure
doc.close
end
end
def closed?
@closed
end
def ensure_not_closed!View on GitHub (pinned to 004a22c1c8)
Solutions
- Call io.rewind and io.binmode, and make sure the IO has not been consumed, before loading
- If the stream is not reliably seekable, read it fully (io.read) and use open_bytes instead
- Confirm io.size matches the actual content length (callback short-reads fail the whole load)
- Rescue Pdfium::PasswordError and retry with the password for encrypted streams
Example fix
# before doc = Pdfium::Document.open_io(socket_io) # after - buffer unreliable streams first data = io.read doc = Pdfium::Document.open_bytes(data)
Defensive patterns
Strategy: validation
Validate before calling
io.binmode io.rewind if io.respond_to?(:rewind) raise ArgumentError, 'IO must expose #size' unless io.respond_to?(:size) && io.size.positive? # if the stream may be short/non-seekable, buffer it instead: # Pdfium::Document.open_bytes(io.read)
Try / catch
begin Pdfium::Document.open_io(io, password) rescue Pdfium::PasswordError retry_with_password rescue Pdfium::PdfiumError => e data = io.rewind && io.read # salvage: retry once from buffered bytes data ? Pdfium::Document.open_bytes(data, password) : raise end
Prevention
- Remember open_io silently delegates to open_file when the IO responds to path; flush before calling
- Use open_io only for genuinely seekable, binary IOs; buffer network streams first
- Do not reuse an IO that a previous parse consumed
When it happens
Trigger: An IO whose read(size) returns fewer bytes than requested (socket closed mid-read, wrapper stream exhausted); io.size not matching the true byte length; a non-seekable pipe; a stream another layer already consumed; an encrypted stream without a password.
Common situations: Streaming HTTP downloads straight into PDFium; reading from decoding wrapper IOs; StringIO fixtures with wrong size; reusing an IO after an earlier parse consumed it.
Related errors
- Failed to load document from file '#{file_path}', pointer is
- Failed to load document from memory, pointer is NULL.
- Failed to create new document
- Failed to save document
- Failed to load page #{page_index}, pointer is NULL.
AI-assisted analysis of docusealco/docuseal@004a22c1c8 (2026-08-21).
Data as JSON: /api/errors/b9f46871b5e5a0a9.
Report an issue: GitHub.