firecrawl/pdf-inspector · error · napi::Error (Status::GenericFailure)

{ctx}: {e}

Error message

{ctx}: {e}

What it means

to_napi_err in napi/src/lib.rs wraps any Rust-side failure (Display error) into a NAPI Error with Status::GenericFailure and the message "{ctx}: {e}". The napi bindings use it at every FFI boundary so a Rust error surfaces in Node.js as a generic JavaScript Error rather than a panic or abort. The ctx prefix tells you which binding operation (e.g. extract, detect) failed.

Source

Thrown at napi/src/lib.rs:417

        pages_with_columns: result.pages_with_columns,
        is_complex: result.is_complex,
        processing_time_ms: timing_ms(result.processing_time_ms),
        render_time_ms: timing_ms(result.render_time_ms),
        ocr_time_ms: timing_ms(result.ocr_time_ms),
    }
}

fn convert_item_type(t: &pdf_inspector::types::ItemType) -> (ItemType, Option<String>) {
    match t {
        pdf_inspector::types::ItemType::Text => (ItemType::Text, None),
        pdf_inspector::types::ItemType::Image => (ItemType::Image, None),
        pdf_inspector::types::ItemType::Link(url) => (ItemType::Link, Some(url.clone())),
        pdf_inspector::types::ItemType::FormField => (ItemType::FormField, None),
    }
}

fn to_napi_err(e: impl std::fmt::Display, ctx: &str) -> Error {
    Error::new(Status::GenericFailure, format!("{ctx}: {e}"))
}

/// Run a closure, catching any Rust panic and converting it to a NAPI error.
/// Prevents process abort from unwind panics in the native module.
fn catch_panic<F, T>(ctx: &str, f: F) -> Result<T>
where
    F: FnOnce() -> Result<T> + panic::UnwindSafe,
{
    match panic::catch_unwind(f) {
        Ok(result) => result,
        Err(payload) => {
            let msg = if let Some(s) = payload.downcast_ref::<&str>() {
                s.to_string()
            } else if let Some(s) = payload.downcast_ref::<String>() {
                s.clone()
            } else {
                "unknown panic".to_string()
            };

View on GitHub (pinned to 636ca1a58b)

Solutions

  1. Read the ctx prefix in the message to identify which binding call failed, then check the arguments passed to that call from JavaScript.
  2. Validate inputs in JS before calling: file exists and readable (fs.accessSync), buffer is a PDF (%PDF- header), options match the documented shape.
  3. Log the full error message — the part after the first ': ' is the original Rust error with the concrete cause.
  4. Update the napi package and native binary to matching versions, since a mismatched .node binary can cause argument conversion failures.

Example fix

// before
const md = pdf2md.processFileSync('/tmp/doc.pdf');
// after
if (!fs.existsSync('/tmp/doc.pdf')) throw new Error('file missing');
if (!fs.readFileSync('/tmp/doc.pdf').subarray(0,5).toString().startsWith('%PDF-')) throw new Error('not a PDF');
const md = pdf2md.processFileSync('/tmp/doc.pdf');
Defensive patterns

Strategy: try-catch

Validate before calling

const fs = require('fs');
function assertPdfInput(path, options) {
  fs.accessSync(path, fs.constants.R_OK);
  const head = fs.readFileSync(path).subarray(0, 5).toString();
  if (!head.startsWith('%PDF-')) throw new Error('not a PDF: ' + path);
  if (options != null && typeof options !== 'object') throw new Error('options must be an object');
}

Type guard

function isNapiError(e) {
  return e instanceof Error && typeof e.message === 'string' && !e.message.includes('Rust panic:');
}

Try / catch

try {
  const md = pdf2md.processFileSync(path, opts);
} catch (e) {
  const [ctx, cause] = String(e.message).split(/: (.+)/);
  console.error(`binding ${ctx} failed:`, cause ?? e.message);
  // fall back or rethrow based on ctx
}

Prevention

When it happens

Trigger: Calling any pdf2md NAPI binding (extract/process/detect functions) whose underlying Rust call returns Err — e.g. invalid PDF bytes, unreadable file path, malformed options passed through to_napi_err.

Common situations: Passing a non-existent file path from Node.js; passing a corrupted or password-protected PDF; passing options of the wrong shape that fail Rust-side validation; running the native module on a platform where an internal syscall fails.

Related errors


AI-assisted analysis of firecrawl/pdf-inspector@636ca1a58b (2026-09-05). Data as JSON: /api/errors/1d0597d74050c582. Report an issue: GitHub.