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.
open Js_of_ocaml
let safe_parse json_str =
try
Some (Js._JSON##parse (Js.string json_str))
with Js_error.Exn err ->
Printf.eprintf "Parse error: %s\n" (Js_error.to_string err);
NoneInspecting 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") stackCatching specific error types
To distinguish between error types, check the error name:
let handle_js_call () =
try
some_js_operation ()
with Js_error.Exn err ->
match Js_error.name err with
| "TypeError" -> (* handle type error *)
| "RangeError" -> (* handle range error *)
| "SyntaxError" -> (* handle syntax error *)
| _ -> (* handle other errors *)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
nCreating typed errors
JavaScript has several built-in error types. You can create them using their constructors:
(* TypeError *)
let type_error_constr : (Js.js_string Js.t -> Js.error Js.t) Js.constr =
Js.Unsafe.global##._TypeError
let raise_type_error msg =
let err = new%js type_error_constr (Js.string msg) in
Js_error.raise_ (Js_error.of_error err)
(* RangeError *)
let range_error_constr : (Js.js_string Js.t -> Js.error Js.t) Js.constr =
Js.Unsafe.global##._RangeError
let raise_range_error msg =
let err = new%js range_error_constr (Js.string msg) in
Js_error.raise_ (Js_error.of_error err)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 - they are represented as JavaScript arrays.
let divide x y =
if y = 0 then failwith "Division by zero"
else x / y
let () = Js.export "divide" divideFrom 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"]
}Raising proper JavaScript errors
For better JavaScript interoperability, raise JavaScript errors instead of OCaml exceptions:
let divide_safe x y =
if y = 0 then
let err = new%js Js.error_constr (Js.string "Division by zero") in
Js_error.raise_ (Js_error.of_error err)
else
x / y
let () = Js.export "divide" divide_safeNow JavaScript receives a proper Error:
try {
divide(10, 0);
} catch (e) {
console.log(e instanceof Error); // true
console.log(e.message); // "Division by zero"
}Preserving stack traces
Attaching JavaScript backtraces
Use Js_error.attach_js_backtrace to attach a JavaScript stack trace to an OCaml exception. This is useful for debugging:
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 exnExtracting JavaScript backtraces
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 of stack traces
Js_of_ocaml can automatically attach a JavaScript Error (which embeds the current stack trace) when raising an OCaml exception. This happens 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
Unhandled OCaml exceptions will throw any attached JavaScript Error, allowing the browser or Node.js to display the stack trace nicely.
Note: Creating JavaScript Error objects is costly and can degrade performance significantly. This is why automatic attachment is not enabled by default.