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
- Read the ctx prefix in the message to identify which binding call failed, then check the arguments passed to that call from JavaScript.
- Validate inputs in JS before calling: file exists and readable (fs.accessSync), buffer is a PDF (%PDF- header), options match the documented shape.
- Log the full error message — the part after the first ': ' is the original Rust error with the concrete cause.
- 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
- Always validate file existence and PDF magic bytes before calling bindings.
- Keep the napi JS package and the compiled .node binary versions in sync.
- Match options objects against the documented TypeScript definitions (strict typing / zod schema).
- Include the full error message in logs — the ctx prefix pinpoints the failing operation.
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.