run-llama/liteparse · critical

failed to load pdfium shared library

Error message

failed to load pdfium shared library

What it means

`Library::init()` acquires the process-wide PDFium lock and initializes PDFium, but first loads the shared library via `pdfium_sys::dynamic::load_default()`. When no loadable libpdfium can be found, `try_init()` returns `PdfiumError::LibraryUnavailable` and `init()` panics with this message. It means the pdfium shared library is missing, corrupt, or its dependencies cannot be resolved on this machine.

Solutions

  1. Set the PDFIUM_LIB_PATH env var to the directory containing the pdfium shared library (libpdfium.so / libpdfium.dylib / pdfium.dll) and retry.
  2. Download the matching pdfium binary distribution for your platform (or use the build-script download path) and place it in one of the searched locations.
  3. In panic-sensitive hosts (Node addon, FFI boundary), call `Library::try_init()` instead of `Library::init()` and handle `PdfiumError::LibraryUnavailable` gracefully.
  4. If the file exists but still fails, run `ldd`/`otool -L` on the library to find missing transitive dependencies and install them, and confirm the architecture matches your build target.

Example fix

// before
let lib = Library::init(); // panics: failed to load pdfium shared library
// after (or: export PDFIUM_LIB_PATH=/opt/pdfium/lib before running)
let lib = Library::try_init().map_err(|e| {
    eprintln!("pdfium unavailable: {e:?}; set PDFIUM_LIB_PATH to the lib dir");
    e
})?;
Defensive patterns

Strategy: fallback

Validate before calling

// check availability without panicking
match Library::try_init() {
    Ok(lib) => { /* proceed */ }
    Err(PdfiumError::LibraryUnavailable) => {
        eprintln!("pdfium missing; set PDFIUM_LIB_PATH to the dir containing libpdfium.so");
    }
    Err(e) => { /* other error */ }
}

Type guard

// Rust has no runtime type guard here; narrow via the typed error enum
fn is_library_unavailable(e: &PdfiumError) -> bool {
    matches!(e, PdfiumError::LibraryUnavailable)
}

Try / catch

// avoid init()'s panic; use try_init and handle the error
let lib = Library::try_init()
    .map_err(|e| anyhow!("pdfium shared library unavailable: {e:?}. Set PDFIUM_LIB_PATH."))?;

Prevention

When it happens

Trigger: Calling `Library::init()` (or anything that triggers it, e.g. `Document` loading) on a machine where `load_default()` fails: PDFIUM_LIB_PATH unset and no downloaded/cached/system libpdfium present; the library exists but has unresolved native dependencies (e.g. missing glibc/libjpeg on Linux); wrong architecture (x86_64 binary trying to load arm64 pdfium).

Common situations: Deploying to a slim Docker image or fresh CI machine without downloading the pdfium binary; PDFium downloaded for a different OS/arch; a Node addon host where the native pdfium library was not bundled; cross-platform distribution where the shared library was not shipped next to the executable.

Related errors


AI-assisted analysis of run-llama/liteparse@22d2dd8cd7 (2026-09-08). Data as JSON: /api/errors/f9b3e89a8e2f161d. Report an issue: GitHub.

Appendix: source

Thrown at crates/pdfium/src/library.rs:69

/// // `lib` was dropped above — using `doc` here is a use-after-unlock.
/// let _ = doc.page_count();
/// ```
pub struct Library {
    #[cfg(not(target_arch = "wasm32"))]
    _guard: MutexGuard<'static, ()>,
    #[cfg(target_arch = "wasm32")]
    _private: (),
}

impl Library {
    /// Acquire the process-wide PDFium lock, blocking the current thread
    /// until any other in-flight PDFium work has finished. Initializes the
    /// library on first call.
    ///
    /// Multiple concurrent callers are serialized; only one `Library`
    /// instance exists at a time.
    pub fn init() -> Library {
        Self::try_init().expect("failed to load pdfium shared library")
    }

    /// [`Library::init`] that reports a missing or unloadable pdfium shared
    /// library as [`PdfiumError::LibraryUnavailable`] instead of panicking.
    /// Hosts that cannot afford a panic across an FFI boundary (a Node addon,
    /// where an escaping panic aborts the process) should call this first;
    /// the search path is described on `pdfium_sys::dynamic::load_default`.
    pub fn try_init() -> Result<Library, PdfiumError> {
        #[cfg(not(target_arch = "wasm32"))]
        {
            pdfium_sys::dynamic::load_default().map_err(|_| PdfiumError::LibraryUnavailable)?;
            // Recover from poisoning: a panic mid-FFI may leave PDFium in
            // an odd state, but subsequent calls should still be allowed
            // (the worst case is that the next parse also fails cleanly).
            let guard = pdfium_lock()
                .lock()
                .unwrap_or_else(|poisoned| poisoned.into_inner());
            INIT.call_once(|| unsafe { ffi!(FPDF_InitLibrary()) });

View on GitHub (pinned to 22d2dd8cd7)