Warning: Reason support is experimental. We are looking for beta-tester and contributors.

Error handling between OCaml and JavaScript

This page explains how exceptions propagate between OCaml and JavaScript code, and how to properly handle errors in both directions.

Catching JavaScript exceptions in OCaml

When JavaScript code throws an exception and it propagates to OCaml code, it is wrapped in the Js_error.Exn exception, which holds a value of type Js_error.t:

open Js_of_ocaml

let safe_parse json_str =
  try
    Some (Js._JSON##parse (Js.string json_str))
  with Js_error.Exn err ->
    (* err has type Js_error.t *)
    Printf.eprintf "Parse error: %s\n" (Js_error.to_string err);
    None

Inspecting JavaScript errors

The Js_error module provides functions to inspect error details:

let handle_error err =
  (* Error message *)
  let msg = Js_error.message err in

  (* Error name (e.g., "TypeError", "ReferenceError") *)
  let name = Js_error.name err in

  (* Stack trace (if available) *)
  let stack = Js_error.stack err in

  Printf.eprintf "Error [%s]: %s\n" name msg;
  Option.iter (Printf.eprintf "Stack:\n%s\n") stack

Raising JavaScript errors from OCaml

To throw a JavaScript error that JavaScript code can catch:

let validate_positive n =
  if n < 0 then
    let err = new%js Js.error_constr (Js.string "Value must be positive") in
    Js_error.raise_ (Js_error.of_error err)
  else
    n

Exception propagation in exported functions

When you export OCaml functions to JavaScript, uncaught OCaml exceptions will propagate to JavaScript. However, OCaml exceptions are not JavaScript Error objects (see runtime representation).

let divide x y =
  if y = 0 then failwith "Division by zero"
  else x / y

let () = Js.export "divide" divide

From JavaScript, you can catch the exception, but it won't be an Error:

try {
  divide(10, 0);
} catch (e) {
  // e is an array, not an Error object
  // e.message is undefined
  console.log(e);  // [0, [0, "Failure", ...], "Division by zero"]
}

For better JavaScript interoperability, raise JavaScript errors instead of OCaml exceptions

Stack traces for OCaml exceptions

By default, OCaml exceptions don't carry JavaScript stack traces. This makes debugging difficult since you can't see where an exception originated in browser DevTools or Node.js.

The solution is to attach a JavaScript Error object to an OCaml exception. JavaScript Error objects capture the call stack at the point of creation, so attaching one to an OCaml exception preserves the stack trace for debugging.

Manual attachment

Use Js_error.attach_js_backtrace to attach a JavaScript stack trace to an OCaml exception:

let process data =
  try
    do_something data
  with exn ->
    let exn = Js_error.attach_js_backtrace exn ~force:false in
    (* Log or rethrow with JS backtrace attached *)
    raise exn

The ~force:false parameter only attaches a new error if one isn't already present. Use ~force:true to always attach a fresh stack trace.

Extracting attached errors

Use Js_error.of_exn to extract the JavaScript error from an OCaml exception:

let log_with_backtrace exn =
  match Js_error.of_exn exn with
  | Some js_err ->
      Printf.eprintf "JS Error: %s\n" (Js_error.to_string js_err);
      Option.iter (Printf.eprintf "Stack:\n%s\n") (Js_error.stack js_err)
  | None ->
      Printf.eprintf "OCaml Error: %s\n" (Printexc.to_string exn)

Automatic attachment

Js_of_ocaml can automatically attach a JavaScript Error whenever an OCaml exception is raised. This is convenient but has a performance cost since creating Error objects is expensive.

Automatic attachment is enabled when:

  1. Printexc.backtrace_status() = true, and
  2. Either:
  • The environment variable OCAMLRUNPARAM contains b=1, or
  • The program was compiled with --enable with-js-error

Example with environment variable:

OCAMLRUNPARAM=b=1 node program.js

Example with compiler flag:

js_of_ocaml --enable with-js-error program.byte

When an unhandled OCaml exception has an attached JavaScript Error, it will be thrown as that error, allowing browsers and Node.js to display a proper stack trace.