============== Error handling ============== In programming, error handling is a complex topic with many pitfalls, and it can be even more daunting when you're writing code within the constraints of a framework, as the way you handle errors needs to mesh with the way the framework dispatches errors and vice versa. This article paints the broad strokes of how errors are handled by the JavaScript framework and Owl, and gives some recommendations on how to interface with these systems in a way that avoids common problems. Errors in JavaScript ==================== Before we dive into how errors are handled in Odoo as well as how and where to customize error handling behavior, it's a good idea to make sure we're on the same page when it comes to what we mean exactly by "error", as well as some of the peculiarities of error handling in JavaScript. The `Error` class ----------------- The first thing that may come to mind when we talk about error handling is the built-in `Error` class, or classes that extend it. In the rest of this article, when we refer to an object that is an instance of this class, we will use the term *Error object* in italics. Anything can be thrown ---------------------- In JavaScript, you can throw any value. It is customary to throw *Error objects*, but it is possible to throw any other object, and even primitives. While we don't recommend that you ever throw anything that is not an *Error object*, the Odoo JavaScript framework needs to be able to deal with these scenarios, which will help you understand some design decisions that we've had to make. When instanciating an *Error object*, the browser collects information about the current state of the "call stack" (either a proper call stack, or a reconstructed call stack for async functions and promise continuations). This information is called a "stack trace" and is very useful for debugging. The Odoo framework displays this stack trace in error dialogs when available. When throwing a value that is not an *Error object*, the browser still collects information about the current call stack, but this information is not available in JavaScript: it is only available in the devtools console if the error is not handled. Throwing *Error objects* enables us to show more detailed information, which a user will be able to copy/paste if needed for a bug report, but it also makes error handling more robust as it allows us to filter errors based on their class when handling them. Unfortunately, JavaScript does not have syntactic support for filtering by error class in the catch clause, but you can relatively easily do it yourself: .. code-block:: javascript try { doStuff(); } catch (e) { if (!(e instanceof MyErrorClass)) { throw e; // caught an error we can't handle, rethrow } // handle MyErrorClass } Promise rejections are errors ----------------------------- During the early days of Promise adoption, Promises were often treated as a way to store a disjoint union of a result and an "error", and it was pretty common to use a Promise rejection as a way to signal a soft failure. While it might seem like a good idea at first glance, browsers and JavaScript runtimes have long started to treat rejected Promises the same way as thrown errors in pretty much every way: - throwing in an async function has the same effect as returning a Promise that is rejected with the thrown value as its rejection reason. - catch blocks in async functions catch rejected Promises that were awaited in the corresponding try block. - runtimes collect stack information about rejected promises. - a rejected Promise that is not caught synchronously dispatches an event on the global/window object, and if `preventDefault` is not called on the event, browsers log an error, and standalone runtimes like node kill the process. - the debugger feature "pause on exceptions" pauses when Promises are rejected For these reasons, the Odoo framework treats rejected Promises in the exact same way as thrown errors. Do not create rejected promises in places where you would not throw an error, and always reject Promises with *Error objects* as their rejection reason. `error` events are not errors ----------------------------- With the exception of `error` events on the window, `error` events on other objects such as ``, `