Creating your own errors
In some browsers, using the native Error
class enables cool features like automatic stack trace.
Just in case you need special kind of Error
(ie: subclasses), I recommend doing it that way:
function SomeError(message) {
// the real instance should be an Error:
var self = new Error(message);
// but the prototype could be updated
if(self.__proto__) { self.__proto__ = SomeError.protoype }
else { copyProperties(SomeError.prototype, self); }
// do some more custom things
// ...
// return the instance
return self;
}
SomeError.prototype = Object.create(Error.prototype);
throw new SomeError('abc');
Woah, François, that seems pretty overcomplicated; why not replace everything inside the constructor with this.message = message
? It has the same effect in the browsers I've seen. (Also don't forget SomeError.prototype.constructor = SomeError
.)
Anyway, to Jonas's point: I think DOMError
is actually pretty OK. One thing I noticed is that the ES5 spec (and presumably ES6, although I haven't checked) is careful to never throw simple Error
objects, but instead TypeError
s or RangeError
s or so on. I infer that they are leaving bare Error
for user cases. I think "the platform" should generally follow this lead; that is, the DOM should leave bare Error
s to the user, and throw DOMError
s or TypeError
s or RangeError
s for its own errors.
To answer your more general question, authors are strongly discouraged from ever throwing anything that is not instanceof Error
, see e.g. Guillermo Rauch's A String is not an Error. Also, the name
property is generally the same across all instances of a particular error type, i.e.
errInstance.name === errInstance.constructor.name === errInstance.constructor.prototype.name === Object.getPrototypeOf(errInstance).name
(You can verify this for all the built-in ES5 errors.) Usually a code
property is used for more specific information, from what I've seen. But yes, that has to be added manually with a bit of awkwardness, i.e.
const e = new DOMError("The message");
e.code = "TimeoutError";
throw e;
As an example from the field, here is how we extend our own error types: imvu/imvujs/blob/master/src/error.js
You can either switch on e.name
or use e instanceof ErrorType
in your catch
handler.
Like Francois's approach, it also preserves the stack
property if the
browser supports it.
Note that
(new DOMError) instanceof Error;
returns false
. So the DOM does do the "strongly discouraged" thing. Is
this ok or bad?
Also, the DOM does not create a new class for each different value of .name
.
I.e. you can get a DOMError
whose .name
is "NetworkError"
or
"AbortError"
. DOMError
even has a constructor which allows setting
.name
to anything: new DOMError(name, message);
In fact, there are no defined situations where the DOM creates
DOMError
objects whose .name
is "DOMError"
.
Again, is this ok or is it bad practice?
Jonas, DOM never throws DOMError. DOMErrors are used for other error mechanisms, such as callbacks.
The DOM spec does however throw DOMException objects. These are instanceof Error (see WebIDL)
var ex;
try {
document.appendChild(document);
} catch (e) {
ex = e;
}
assert(ex instanceof Error);
assert(ex.contructor === DOMException);
assert(ex.name === 'HierarchyRequestError');
In Blink DOMException also have a stack property
assert(stack in ex);
From: Jonas Sicking [jonas at sicking.cc]
Note that
(new DOMError) instanceof Error;
returns false. So the DOM does do the "strongly discouraged" thing. Is this ok or bad?
This is horrible! I had no idea! It should definitely derive from Error
.
Also, the DOM does not create a new class for each different value of .name.
I.e. you can get a DOMError whose .name is "NetworkError" or "AbortError".
In fact, there are no defined situations where the DOM creates DOMError objects whose .name is "DOMError".
Again, is this ok or is it bad practice?
This seems bad, although in practice not nearly as bad as the broken inheritance. (I guess because getting name
correct is subtle; many user-space custom errors do not do so, so you can't always rely on it.) In an ideal world, I think you could do a few things:
- A hierarchy, where e.g.
NetworkError
derives fromDOMError
derives fromError
, and all have the correctname
properties. - A reform, where
DOMError
derives fromError
and both have the correctname
properties; but, specificDOMError
instances thrown by certain APIs can have acode
property containing strings like"NetworkError"
.
DOMError even has a constructor which allows setting .name to anything: new DOMError(name, message);
This seems quite bad: error constructors always (in ES)/almost always (in user space) take message
as their first parameter, with other parameters beyond that being introduced by various user-space errors.
On Jul 10, 2013, at 12:55 PM, Domenic Denicola wrote:
From: Jonas Sicking [jonas at sicking.cc]
DOMError even has a constructor which allows setting .name to anything: new DOMError(name, message);
This seems quite bad: error constructors always (in ES)/almost always (in user space) take
message
as their first parameter, with other parameters beyond that being introduced by various user-space errors.
IE has a legacy feature that is similar but different which also necessitated a change in the Error constructor:
2.1.142 [ECMA-262] Section 15.11.2.1, new Error (messageOrNumber)
V0214:
When the Error constructor is called with one argument the following steps are taken:
1. The [[Prototype]] property of the newly constructed object is set to the original Error prototype object, the one that is the initial value of Error.prototype ([ECMA-262] section 15.11.3.1).
2. The [[Class]] property of the newly constructed Error object is set to "Error".
3. Let message be the empty string.
4. Let number be NaN.
5. If messageOrNumber is undefined, then go to step 8.
6. Let number be ToNumber(messageOrNumber).
7. If number is not NaN, then go to step 9.
8. Let message be ToString(messageOrNumber).
9. The description property of the newly constructed object is set to message.
10. If the argument message is not undefined, the The message property of
the newly constructed object is set to ToString(message).
11. The name property of the newly constructed object is set to "Error".
12. If number is NaN, then go to step 14.
13. The number property of the newly constructed object is set to number.
14. Return the newly constructed object.
I really don't think you want to go down that path...
I am not sure where this conversation ended up so I hope I am not repeating already said stuff but you can always create constructors that inherits from Error and set the name.
function CustomError(message) {
this.message = message || '';
}
CustomError.prototype = new Error;
CustomError.prototype.name = CustomError.name;
CustomError.prototype.constructor = CustomError;
// whenever you need
throw new CustomError;
That will be highlighted in most consoles as red CustomError instance you can inspect to read the message.
Hope this helped anyhow.
Best
function CustomError(message) {
this.message = message || '';
}
CustomError.prototype = new Error;
// whenever you need
throw new CustomError;
At best, this will not preserve the stack trace property, at worse this will lead to a bad one.
At best, this will not preserve the stack trace property, at worse this will lead to a bad one.
Because the location info will be that of the new Error
? One could
try this instead, which seems to work for me:
throw { __proto__ : new Error(), reason: "Monday" }
Because the location info will be that of the
new Error
? One could try this instead, which seems to work for me:
throw { __proto__ : new Error(), reason: "Monday" }
That one seems to be working (except in IE<11 since IE didn't support __proto__
prior to that). That somehow surprises me that browser "unwrap" the prototype chain but they seem to do in this case.
yep, I thought it was about having meaningful red errors with a proper name
in console. The stack can be addressed passing the new CustomError(new Error('message'))
using error as argument, not big deal ... abusing __proto__
for this seems
like the wrong answer to a concrete problem
What's wrong with:
throw new CustomError('message')
function CustomError(message /* accept any custom arguments here */) {
Error.call(this, message)
this.name = 'CustomError'
/**
* add any custom error info here
*/
}
CustomError.prototype = Object.create(Error)
CustomError.prototype.constructor = CustomError
In ES6 land this becomes:
throw new CustomError('message')
class CustomError extends Error {
constructor (message /* accept any custom arguments here */) {
super(message)
this.name = 'CustomError'
/**
* add any custom error info here
*/
}
}
Doesn't that seem like an obvious enough solution?
wrong at least two things for the non ES6 version
Error.call(this, message)
does not do what you think it should doCustomError.prototype = Object.create(Error)
has a typo, you meantObject.create(Error.prototype)
In the ES6 version, I am not sure about constructors and classes, but name should be inherited or the console won't even look for it as own property so it should be
class CustomError extends Error {
name = 'CustomError'
constructor (message /* accept any custom arguments here */) {
super(message)
}
}
only in case CustomError won't have already a name
property as it is for
ES3<=ES5 constructors.
:-)
You need to say:
class CustomError extends Error {
get name() {return 'CustomError'}
constructor (message /* accept any custom arguments here */) {
super(message)
}
}
or alternatively:
class CustomError extends Error {
constructor (message /* accept any custom arguments here */) {
super(message)
}
}
CustomError.prototype.name = 'CustomError';
This is one of the rare places where it is inconvenient that class declarations don't provide syntax for defining data properties on the prototype.
Forbes class version should be fine if you don't mind each CustomError instance having an own 'name' data property.
Erik Arvidsson wrote:
DOMErrors are used for other error mechanisms, such as callbacks. [1]
Indeed. But this is one of the things I'm trying to fix. It seems very silly that the DOM defines two classes of error objects. Neither of which are following the patterns used by ES.
The perpetual hazard with trying to clean things up is, of course, legacy compat constraints. Regarding ES itself, many of those on this list (including myself) feel oriented about when and where these bite and how much, and have successfully navigated through these to many successful cleanups. We have also succeeded at not wasting too much time on attractive cleanups whose adoption was doomed because of these constraints.
Regarding DOM, I am certainly less oriented and I suspect at least some others on this list are too. In the absence of knowledge, it is tempting to imagine the worst and despair of cleaning up anything that is already deployed. Could you give a summary -- necessarily subjective which is fine -- of how you think about what can and cannot be successfully cleanup up here? Thanks.
oh well ... I've missed the part when you guys decided that inherited defaults are useless as properties ... :-/
as you said, inconvenient, and not only this case ... also good to know ^_^
I think we have the following constraints for exceptions thrown by the DOM:
x instanceof DOMException
This probably needs to evaluate to true.
x.code == DOMException.HIERARCHY_REQUEST_ERR
x.code == 3
This definitely needs to evaluate to true for a bunch of older APIs. This used to be the only way to test what type of exception you had. For newer exception types we haven't added a code and so .code returns 0. For those APIs you use .name instead, see below. Obviously the exact constant and number varies with the exception type.
x.name == "HierarchyRequestError"
This should test true. This should be uncontroversial enough that I don't think it matters how much dependency there is, right? Obviously the exact value varies with the exception type.
x.message
This probably needs to contain a human readable error message. This should be uncontroversial enough that I don't think it matters how much dependency there is, right?
Object.toString(x)
I don't think it's important what this returns. I.e. I would guess that we can safely subclass DOMException as needed.
I think we have the following constraints for properties that contain error information in APIs that currently use DOMError (IDBTransaction.error in the IndexedDB spec for example).
x instanceof DOMError
Probably doesn't matter what this returns.
x.name = "AbortError"
This should be uncontroversial enough that I don't think it matters how much dependency there is, right?
x.message
This probably needs to contain a human readable error message. This should be uncontroversial enough that I don't think it matters how much dependency there is, right?
Object.toString(x)
Probably doesn't matter what this returns.
I doubt that in either case it matters if these properties live on the objects themselves, or on the prototype chain.
So all in all, I think we have a lot of room for what to do in both
cases. The only requirements is that exceptions likely need to test
true for instanceof DOMException
, and that they in some cases need
to have a specific numeric code property.
My recommendation would be to
- Get rid of DOMError and use DOMException in its place
- Add new subclasses for each exception type. I.e. exceptions with
.name == HierarchyRequestError
should also test true forinstanceof HierarchyRequestError
- Keep specifying that DOMException is a subclass of Error. This is already the case, but I wanted to make it clear that this shouldn't change.
I personally think that the second bullet is a bit silly. But since that appears to be the convention that ES uses then we should stick with it.
On Tue, Jul 16, 2013 at 4:30 PM, Jonas Sicking <jonas at sicking.cc> wrote:
Object.toString(x)
I assume you mean Object.prototype.toString.call(x)
x instanceof DOMError
Probably doesn't matter what this returns.
FWIW, Neither Blink nor WebKit exposes DOMError
so this should be safe
to change (on the open web at least).
I personally think that the second bullet is a bit silly. But since that appears to be the convention that ES uses then we should stick with it.
Agreed.
There will be issues here since DOM has throw a SyntaxError
, which
means throw a DOMException
with the name SyntaxError
and the code
DOMException.SYNTAX_ERR
so that would mean that we have a naming
conflict since ES already have a SyntaxError
constructor.
On Tue, Jul 16, 2013 at 5:05 PM, Erik Arvidsson <erik.arvidsson at gmail.com> wrote:
There will be issues here since DOM has throw a
SyntaxError
, which means throw aDOMException
with the nameSyntaxError
and the codeDOMException.SYNTAX_ERR
so that would mean that we have a naming conflict since ES already have aSyntaxError
constructor.
Good point. It's entirely plausible that we could simply use the ES
SyntaxError
in all cases where DOMException
SyntaxError
is being
thrown today. Would need to be tested though.
Just to relay here what I discussed with Allen in the off-periods of the last TC39 meeting.
On Wed, Jul 17, 2013 at 12:30 AM, Jonas Sicking <jonas at sicking.cc> wrote:
x.name == "HierarchyRequestError" This should test true. This should be uncontroversial enough that I don't think it matters how much dependency there is, right? Obviously the exact value varies with the exception type.
Given that for every JavaScript exception x.name is the name of the object, violating that is actually breaking a pattern. x.name should be DOMException. We could have x.subname that would be something more specific.
I think we have the following constraints for properties that contain error information in APIs that currently use DOMError (IDBTransaction.error in the IndexedDB spec for example).
x.name = "AbortError" This should be uncontroversial enough that I don't think it matters how much dependency there is, right?
This has the same problem. It might be worse here.
My recommendation would be to
- Get rid of DOMError and use DOMException in its place
Agreed.
- Add new subclasses for each exception type. I.e. exceptions with .name == HierarchyRequestError should also test true for "instanceof HierarchyRequestError"
Allen and I both agreed this would be overkill. Using .subname or some such should be sufficient.
- Keep specifying that DOMException is a subclass of Error. This is already the case, but I wanted to make it clear that this shouldn't change.
Agreed.
On Wed, Jul 17, 2013 at 7:10 PM, Jonas Sicking <jonas at sicking.cc> wrote:
Good point. It's entirely plausible that we could simply use the ES SyntaxError in all cases where DOMException SyntaxError is being thrown today. Would need to be tested though.
SyntaxError is used by JavaScript for syntax errors in JavaScript. We shouldn't use it for where we throw "SyntaxError" in platform APIs today per discussion with Allen. (He considered the usage for JSON already as a mistake.)
Problem is, "TypeError" for what Python calls "ValueError", what JS might call "DomainError" to go with "RangeError", is lame. Allen, Rick, I forget: have we discussed DomainError or ValueError?
Is a DomainError the dual of a RangeError?
We did discuss this, as record in ecmascript#224 , and concluded that we it we didn't want to add any new built-in exceptions. Of the existing exceptions , RangeError is closest in concept to what might be described as ValueError.
Allen Wirfs-Brock wrote:
We did discuss this, as record in ecmascript#224 , and concluded that we it we didn't want to add any new built-in exceptions. Of the existing exceptions , RangeError is closest in concept to what might be described as ValueError.
I'd be ok with adding DomainError -- dual, as Mark says -- cheap one-time addition, not repeated, sold out performance, retired and tax fugitive after ;-).
I actually had ValueError in the 2011-10-31 draft of ECMA-402; the TC39 meeting on 2011-11-16 decided against that.
globalization:working_draft_ecmascript_globalization_api_2011-10-31.pdf
esdiscuss/2011-November/018507
Norbert
Correction: The November meeting didn't quite decide; there was more discussion on es-discuss and at the TC39 meeting on 2012-01-19: esdiscuss/2011-November/thread.html#18685, esdiscuss/2012-January/019784
Norbert
On Aug 6, 2013, at 12:40 PM, Brendan Eich wrote:
Allen Wirfs-Brock wrote:
We did discuss this, as record in ecmascript#224 , and concluded that we it we didn't want to add any new built-in exceptions. Of the existing exceptions , RangeError is closest in concept to what might be described as ValueError.
I'd be ok with adding DomainError -- dual, as Mark says -- cheap one-time addition, not repeated, sold out performance, retired and tax fugitive after ;-).
See like a distraction to reopen an issue we already decided.
Also don't really see that it helps much. Right now, API designer have to make a decision about the murky distinction between TypeError (sometimes "type" is interpreted very loosely) and RangeError. Adding DomainError would probably just add to the confusion and in the end it probably makes no difference. Has anybody ever actually seen a JS exception handler that really needs to take conditional action depending upon whether as TypeError or RangeError was thrown?
Even though I contributed the suggestion, I agree with Allen. It is already way too late to pretend that JS is a language in which it is useful to dispatch on the type of error. Given that, let's leave well enough alone and not introduce more error types for any reason other than legacy compat (i.e., with existing DOM practice).
Allen Wirfs-Brock wrote:
On Aug 6, 2013, at 12:40 PM, Brendan Eich wrote:
Allen Wirfs-Brock wrote:
We did discuss this, as record in ecmascript#224 , and concluded that we it we didn't want to add any new built-in exceptions. Of the existing exceptions , RangeError is closest in concept to what might be described as ValueError. I'd be ok with adding DomainError -- dual, as Mark says -- cheap one-time addition, not repeated, sold out performance, retired and tax fugitive after ;-).
See like a distraction to reopen an issue we already decided.
No, it's called revisiting and we do it because we do make mistakes :-P.
This one is too small to be a mistake, I agree, but your "distraction" word is off-target. TC39 has to revisit decided things now and then. We've done it before as Mark well knows.
Also don't really see that it helps much. Right now, API designer have to make a decision about the murky distinction between TypeError (sometimes "type" is interpreted very loosely) and RangeError. Adding DomainError would probably just add to the confusion and in the end it probably makes no difference.
No, it would be for the input/domain side, as range error should be for output/co-domain, where the type agrees but cannot express a value restriction.
If we don't have a principled approach we will go wrong with RangeError. Possibly we already have?
Has anybody ever actually seen a JS exception handler that really needs to take conditional action depending upon whether as TypeError or RangeError was thrown?
That's not the issue. If you don't care about which (whatever the set of standard error types), you might as well toss a coin. But clearly we have some implicit rule or rules informing when to use RangeError vs. TypeError. Or are the rules explicit?
Saying the Java-esque "Error class hierarchy" is not useful for dispatching also ignores matching in catch clauses, on the Harmony agenda.
But even if we think everything here is a mistake, it is at least an attractive nuisance that trips up developers and other standards' caretakers. We need some ground rules and FAQ entries for when to use Type vs. Range, and why-no-Value/Domain.
On Tue, Aug 6, 2013 at 1:02 PM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:
Has anybody ever actually seen a JS exception handler that really needs to take conditional action depending upon whether as TypeError or RangeError was thrown?
For what it's worth, as someone using both promises and custom error types in the field, we do switch on different types of errors. We alternate between instanceof and testing the .name property on the exception as a stylistic choice.
Here's a common idiom (note, I typed this from memory, and haven't compiled it:
fetchServiceResult().then(function(result) { // use result }).catch(function(error) { if (error instanceof NetworkError) { // perhaps retry a couple times // perhaps show a dialog box } else { throw error; // bubble anything else } });
On Tue, Aug 6, 2013 at 10:03 PM, Brendan Eich <brendan at mozilla.com> wrote:
That's not the issue. If you don't care about which (whatever the set of standard error types), you might as well toss a coin. But clearly we have some implicit rule or rules informing when to use RangeError vs. TypeError. Or are the rules explicit?
Saying the Java-esque "Error class hierarchy" is not useful for dispatching also ignores matching in catch clauses, on the Harmony agenda.
But even if we think everything here is a mistake, it is at least an attractive nuisance that trips up developers and other standards' caretakers. We need some ground rules and FAQ entries for when to use Type vs. Range, and why-no-Value/Domain.
And having this sooner rather than later would be welcome. There's new APIs coming up that want to mint new types of errors and it seems we might be stuck with the situation that DOMException.name returns a specific name rather than "DOMException". We can probably still kill DOMError, but the alternative should be clear.
If in the end people are expected to check Error.name I don't think it matters much DOM has a catchall DOMException object that represents a bunch of distinct names.
And FWIW, the distinct names are occasionally useful for certain APIs, e.g. when you want to react differently between a network error and an end-user terminating the operation.
More FWIW, dom.spec.whatwg.org/#errors describes the situation we have today. Timely advice much appreciated.
FWIW for non-DOM code I see people using instanceof
much more often than checking name
.
Thus I personally think that creating a bunch of new DOMException
subtypes would be the way to go, e.g. a PermissionDeniedError
whose .name
is PermissionDeniedError
and whose .__proto__
is DOMException
.
If we were starting from scratch we'd probably just have a single DOMException
(probably named DOMError
actually) with a .code
property that can vary between types of errors. But since we're not, creating a large hierarchy seems best, since it allows you to preserve all the usual properties of JS errors. The only downside of it is that it creates a large hierarchy, which seems like less of a downside than e.g. having an error whose .name
is not equal to its .constructor.name
.
On Wed, Aug 21, 2013 at 12:21 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:
FWIW for non-DOM code I see people using
instanceof
much more often than checkingname
.Thus I personally think that creating a bunch of new
DOMException
subtypes would be the way to go, e.g. aPermissionDeniedError
whose.name
isPermissionDeniedError
and whose.__proto__
isDOMException
.If we were starting from scratch we'd probably just have a single
DOMException
(probably namedDOMError
actually) with a.code
property that can vary between types of errors.
.code
implies numeric error codes which has gone out of fashion.
On Wed, Aug 21, 2013 at 5:21 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:
FWIW for non-DOM code I see people using
instanceof
much more often than checkingname
.
In public-script-coord and elsewhere people have argued that instanceof is an indication of badness: lists.w3.org/Archives/Public/public-script-coord/2013JulSep/0225.html
Thus I personally think that creating a bunch of new
DOMException
subtypes would be the way to go, e.g. aPermissionDeniedError
whose.name
isPermissionDeniedError
and whose.__proto__
isDOMException
.
That seems like a lot of unnecessary bloat for something that's rather trivial.
If we were starting from scratch we'd probably just have a single
DOMException
(probably namedDOMError
actually) with a.code
property that can vary between types of errors. But since we're not, creating a large hierarchy seems best, since it allows you to preserve all the usual properties of JS errors. The only downside of it is that it creates a large hierarchy, which seems like less of a downside than e.g. having an error whose.name
is not equal to its.constructor.name
.
Allen suggested having .subname. But I'm not sure we can still do that and it also seems silly to make these exceptions second-class citizens.
What is the current expected way for an author to throw a custom errors? You obviously could do something like:
throw { reason: "TimeoutError" };
or
throw "TimeoutError";
However that makes it very hard for anyone receiving an exception to inspect what it is. I.e. you'd first have to check what type of value it is, i.e. if it's a string, something that's
instanceof Error
, or something with just custom properties.Instead you can do
throw new Error;
However, as I understand it, that doesn't let you pass machine-readable information in the error. You can only pass a message which is intended for human consumption. I.e. something like:
throw new Error("operation took too long and timed out.");
Yet another alternative is to do
let e = new Error; e.name = "TimeoutError"; throw e;
This is a little bit more code than expected though.
A shorter alternative that is somewhat compatible with Error is
throw { name: "TimeoutError" };
However this means that
instanceof Error
returns false, which could mean that code is forced to check which properties are set.The reason I'm asking is that the DOM has invented a completely new interface,
DOMError
. This seems pretty heavy-handed and yet another instance of the DOM doing it in its own way rather than using existing ES functionality.I'd like for the DOM to mimic what we expect authors to do. It's just not obvious to me what authors are expected to do if they want to throw a machine readable error. I.e. one that allows code to catch the error and handle it.