From 947387a6ba0ed910a06489eb1b83c4ed0d015029 Mon Sep 17 00:00:00 2001 From: Brent Anderson Date: Wed, 18 Mar 2026 10:58:23 -0500 Subject: [PATCH] fix: catch panics in NIF to prevent BEAM crash Wraps the css inlining logic in `std::panic::catch_unwind` so any unexpected Rust panic is converted to a RustlerError instead of aborting the BEAM VM. --- native/css_inline_nif/src/lib.rs | 36 ++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/native/css_inline_nif/src/lib.rs b/native/css_inline_nif/src/lib.rs index d3e7ca6..0579c49 100644 --- a/native/css_inline_nif/src/lib.rs +++ b/native/css_inline_nif/src/lib.rs @@ -33,21 +33,31 @@ struct Options { /// since BEAM-managed data must live on the BEAM heap. #[rustler::nif(schedule = "DirtyCpu")] fn inline_css(html: &str, opts: Options) -> Result, RustlerError> { - let estimated_size = (html.len() as f64 * 1.5) as usize; - let mut buffer: Vec = Vec::with_capacity(estimated_size); - let inliner = CSSInliner::options() - .inline_style_tags(opts.inline_style_tags) - .keep_style_tags(opts.keep_style_tags) - .keep_link_tags(opts.keep_link_tags) - .load_remote_stylesheets(opts.load_remote_stylesheets) - .minify_css(opts.minify_css) - .build(); + std::panic::catch_unwind(|| { + let estimated_size = (html.len() as f64 * 1.5) as usize; + let mut buffer: Vec = Vec::with_capacity(estimated_size); + let inliner = CSSInliner::options() + .inline_style_tags(opts.inline_style_tags) + .keep_style_tags(opts.keep_style_tags) + .keep_link_tags(opts.keep_link_tags) + .load_remote_stylesheets(opts.load_remote_stylesheets) + .minify_css(opts.minify_css) + .build(); - inliner - .inline_to(html, &mut buffer) - .map_err(|e| RustlerError::Term(Box::new(format!("CSS inlining failed: {}", e))))?; + inliner + .inline_to(html, &mut buffer) + .map_err(|e| RustlerError::Term(Box::new(format!("CSS inlining failed: {}", e))))?; - Ok(buffer) + Ok(buffer) + }) + .map_err(|e| { + let msg = e + .downcast_ref::() + .map(|s| s.as_str()) + .or_else(|| e.downcast_ref::<&str>().copied()) + .unwrap_or("unknown panic"); + RustlerError::Term(Box::new(format!("NIF panic: {}", msg))) + })? } rustler::init!("Elixir.CSSInline.Native");