Skip to content

Error Handling

Fresh 🌱

Neon maps Rust's Result onto JavaScript's exception model. A Neon function that returns JsResult<T> either returns a value (Ok) or throws a JavaScript exception (Err). Understanding this mapping is the key to clean error handling.

JsResult and the ? operator

JsResult<'a, T> is an alias for a Rust Result whose error case represents a thrown JavaScript exception. Any operation that can throw (reading an argument, getting a property, calling a function) returns a result, so you propagate failures with the ? operator:

rust
fn read_prop(mut cx: FunctionContext) -> JsResult<JsValue> {
    let obj = cx.argument::<JsObject>(0)?; // throws TypeError if arg 0 is not an object
    let val = obj.get(&mut cx, "name")?;   // propagates any error from the getter
    Ok(val)
}

When ? short-circuits, the pending JavaScript exception is already set; control returns to JS with that exception thrown.

Throwing an exception explicitly

To throw your own error, use the context's throw helpers. throw_error raises a JavaScript Error with a message:

rust
fn must_be_positive(mut cx: FunctionContext) -> JsResult<JsNumber> {
    let n: f64 = cx.argument::<JsNumber>(0)?.value(&mut cx);
    if n < 0.0 {
        return cx.throw_error("expected a non-negative number");
    }
    Ok(cx.number(n.sqrt()))
}

throw_error returns a result in the Err state, so you can return it directly from a function whose body produces the matching Ok type.

Converting a Result at the throw site

When you already hold a Rust Result (for example from standard-library or crate code), ResultExt::or_throw() converts an Err into a thrown JavaScript exception and unwraps the Ok:

rust
let parsed = some_rust_result.or_throw(&mut cx)?;

Errors with #[neon::export]

When you use the #[neon::export] macro with plain Rust types, return a Result and Neon throws on the JavaScript side automatically. The extract::Error type bridges common Rust errors (anything implementing the standard error trait) into JS exceptions:

rust
use neon::types::extract::Error;

#[neon::export]
fn read_file(path: String) -> Result<String, Error> {
    // The `?` converts std::io::Error into a thrown JS exception
    let contents = std::fs::read_to_string(path)?;
    Ok(contents)
}

On the JavaScript side this looks like a normal throwing function:

javascript
try {
  readFile('/no/such/path');
} catch (e) {
  console.error(e.message);
}

Summary

SituationTool
Propagate an error that already occurred? operator on a JsResult
Throw your own errorcx.throw_error("message")
Turn a Rust Result into a throwresult.or_throw(&mut cx)?
Return errors from #[neon::export]Result<T, neon::types::extract::Error>

Errors are values, not panics

Prefer throwing JavaScript exceptions over Rust panic!. A panic across the FFI boundary is far more disruptive than a clean thrown error that JavaScript can catch.