firecrawl/pdf-inspector · error · napi::Error (Status::GenericFailure)
{ctx}: Rust panic: {msg}
Error message
{ctx}: Rust panic: {msg} What it means
catch_panic in napi/src/lib.rs wraps Rust closures so that a panic (unwind) inside the native module is converted into a NAPI Error with message "{ctx}: Rust panic: {msg}" instead of aborting the Node process. Rust panics normally abort or unwind across FFI, which is UB/abort in native modules; this converts them into a catchable JS Error. The payload is the panic message (&str, String, or 'unknown panic').
Source
Thrown at napi/src/lib.rs:436
}
/// 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()
};
Err(Error::new(
Status::GenericFailure,
format!("{ctx}: Rust panic: {msg}"),
))
}
}
}
// ---------------------------------------------------------------------------
// Shared implementations (single body behind sync and async entry points)
// ---------------------------------------------------------------------------
fn process_pdf_impl(bytes: &[u8], pages: Option<Vec<u32>>) -> Result<PdfResult> {
let mut opts = pdf_inspector::PdfOptions::new();
if let Some(p) = pages {
opts = opts.pages(p);
}
let result = pdf_inspector::process_pdf_mem_with_options(bytes, opts)
.map_err(|e| to_napi_err(e, "process_pdf"))?;View on GitHub (pinned to 636ca1a58b)
Solutions
- Capture the message after 'Rust panic:' and reduce the input to a minimal PDF that reproduces it, then file a bug with that file.
- Wrap the binding call in try/catch in Node.js so one bad document doesn't take down the process — this error IS the catchable form of the panic.
- Isolate risky documents in a worker thread/child process so even an abort cannot kill the main process.
- Upgrade the package — panics on valid PDFs are bugs that get fixed; check the changelog for the panic site mentioned in the message.
Example fix
// before
const md = pdf2md.processFileSync(pdfPath);
// after
let md;
try {
md = pdf2md.processFileSync(pdfPath);
} catch (e) {
if (String(e.message).includes('Rust panic:')) {
console.error('native panic on', pdfPath, e.message);
md = null; // skip / quarantine this document
} else { throw e; }
} Defensive patterns
Strategy: try-catch
Validate before calling
function looksLikePdf(buf) {
const b = Buffer.isBuffer(buf) ? buf : Buffer.from(buf);
return b.length > 5 && b.subarray(0, 5).toString('latin1').startsWith('%PDF-');
} Type guard
function isPanicError(e) {
return e instanceof Error && e.message.includes(': Rust panic: ');
} Try / catch
try {
return pdf2md.processFileSync(pdfPath);
} catch (e) {
if (isPanicError(e)) {
quarantine(pdfPath, e.message.split(': Rust panic: ')[1]);
return null; // isolate: don't let one doc break the batch
}
throw e;
} Prevention
- Wrap every native-module call in try/catch — this error is the catchable form of a Rust panic.
- Run batch processing in worker threads or child processes so a true abort cannot kill the main process.
- Quarantine and report any PDF that triggers a panic; panics on valid input are library bugs.
- Keep the native module updated; check the changelog when a panic message mentions a known site.
When it happens
Trigger: Any pdf2md NAPI binding call whose Rust internals hit a panic: unreachable!()/unwrap()/expect failure, index-out-of-bounds, integer overflow in debug, or assertion inside extraction/table/layout code.
Common situations: Processing an unusual or malformed PDF that triggers an unhandled edge case (e.g. corrupt content stream, extreme coordinates); running a release binary compiled with panic=abort bypassing this guard; hitting a library bug on a specific document.
Related errors
AI-assisted analysis of firecrawl/pdf-inspector@636ca1a58b (2026-09-05).
Data as JSON: /api/errors/281491e82ec545b8.
Report an issue: GitHub.