Error objects in W3C APIs
jonas at sicking.cc
Tue Feb 4 01:27:13 PST 2014
We have been debating how to design errors raised by future W3C specs
to look like. This includes both synchronously and asynchronous APIs.
I.e. both thrown exception objects, and asynchronously reported errors
reported by for example Promises.
We'd like to get TC39s input on which direction to go.
A few of the goals that we've had are:
Use the same objects for both synchronous and asynchronous errors.
This simplifies error handling, and allows errors to flow through both
synchronous and asynchronous call chains.
The error objects should test true for |instanceof Error|. I.e. they
should either be of type Error, or of a subclass of it.
We believe there are situations when it's useful to let code detect
what type of error was produced. For example when doing a
XMLHttpRequest, it's useful for the caller to be able to know if there
was a network error, and it should tell the user to check if they are
properly connected to the internet. Or if there was an authentication
error and the user should try a different username/password. Or if
there was some other error which the website should log and send back
to the developer for further analysis.
Even in scenarios when a given API can only produce one type of error,
it can be valuable to include specific code-readable information in
that error. A call chain might call into several different functions
that can fail, and it's convenient if you can place an error handler
at the top of the call stack which handles errors from multiple
different functions. This also applies for asynchronous errors where a
chain of Promise.then() calls can surface errors from many different
operations to a single error handler at the end of the chain.
In situations where it's useful for code to detect which type of error
had happened we want to use the same mechanism as for normal JS
errors, i.e. the .name property. We do not want to always set .name to
something generic like "DOMException" and then add something like
.subname. This seems to create a fork in the design where W3C specs
behaves different from core JS. And it'd also force authors to check
both .name and .subname which seems annoying.
It is a little unclear what backwards compatibility constraints that
we have. Currently many specifications throw DOMExceptions with a
.code and a .name property. The .code is set to a numeric value and
the .name property uses the same style of naming as built-in errors.
For some of these errors we likely have to make |error instanceof
DOMException| test true, and .code return the values they currently
However we might have the flexibility to use whatever class we want,
even for old errors. As long as the class is a subclass of
DOMException and thus the instanceof test above tests true. Also,
DOMException can very likely be made a subclass of Error.
And of course for new errors in specs that have not yet shipped we
have full flexibility to do whatever we want.
So the question is, what should we do?
Option A) Use subclasses of DOMException where web compatibility
requires, and direct subclasses of Error otherwise. So we'd add a
InvalidStateError class which inherits DOMException. But a new
FileIOError could inherit directly from Error. DOMException would
inherit Error which means that all errors would still be instanceof
Option B) Don't use more subclasses. Instead use DOMException or Error
directly. But the .name would contain values like "InvalidStateError"
or "FileIOError". DOMException would still inherit Error, which means
that all errors would be instanceof Error.
Option C is "something else".
The advantage of A is that it follows the pattern of current JS
errors. The disadvantage is that it clutters the global object with a
lot of error constructor functions which doesn't really provide any
value as far as I can tell.
Another advantage with A is that it gives us a prototype object to
stick additional information for certain errors. For example the
ConstraintError reported by IndexedDB might in the future provide
information about which key there was a constraint validation in.
Likewise a NetworkError might want to provide the response body of a
Such information is possible even using option B, but requires that
properties are added on the error object instances, rather than as a
getter on the prototype (which is the pattern used by other properties
on Error subclasses).
I'm personally in favor of option B. It seems simpler while still
retaining the ability to check the error type through a single
operation, i.e. by checking error.name.
But I'm curious to hear others' thoughts. Especially about if there's
any good Option Cs.
More information about the es-discuss