Error objects: RangeError, TypeError, ValueError

# Norbert Lindenberg (14 years ago)

At last week's TC39 meeting, I asked which error objects an API should throw when it expects a string matching a specific pattern, but receives a string that doesn't match the pattern. Examples in the Globalization API are language tags, as defined in RFC 5646, or selectors such as "sort" and "search".

Candidates are:

  • RangeError - "Indicates a numeric value has exceeded the allowable range." The Language Specification uses it only for that purpose.

  • TypeError - "Indicates the actual type of an operand is different than the expected type." Used in the Language Specification not only for differences in "type" as defined in the spec, but also when properties are missing, when objects or properties don't have the attributes needed by an algorithm, or when the [[class]] internal property doesn't have the expected value. The Array.prototype.reduce* functions throw it when a required argument is missing, and the RegExp constructor when an unwanted argument is present. JSON.stringify throws it when given a cyclic structure.

  • ValueError - as introduced in the Globalization API spec.

ValueError would be unnecessary if the definition of RangeError or TypeError were expanded to cover string value mismatches as well.

Which way should we go?

Thanks, Norbert

# Allen Wirfs-Brock (14 years ago)

Range error actually has fairly limited usage in the current specification. Enough so, that it arguably doesn't carry its weight as one of the very few available exceptions "classes". I think we could reasonably drop the "numeric" from its prose description and use it for all (new) situations where a provided value is of the correct "type" but outside the set of acceptable values of that type.

Is there really any situation where it would make sense for an exception handler to filter for numeric range error but would exclude non-numeric range errors? I find it implausible as ES exceptions just aren't that precisely identified.

Unfortunately, as it now stands you have to use the implementation dependent message property value to actually identify specific exceptions.

I wonder if for new exception occurrences (in our specs.) we should specify unique implementation independent name property values. To do so, we would have to develop a meaningful fine-grained naming scheme.

# John J Barton (14 years ago)

On Mon, Nov 21, 2011 at 5:11 PM, Norbert Lindenberg <ecmascript at norbertlindenberg.com> wrote:

At last week's TC39 meeting, I asked which error objects an API should throw when it expects a string matching a specific pattern, but receives a string that doesn't match the pattern. Examples in the Globalization API are language tags, as defined in RFC 5646, or selectors such as "sort" and "search".

Candidates are:

  • RangeError - "Indicates a numeric value has exceeded the allowable range." The Language Specification uses it only for that purpose.

  • TypeError - "Indicates the actual type of an operand is different than the expected type." Used in the Language Specification not only for differences in "type" as defined in the spec, but also when properties are missing, when objects or properties don't have the attributes needed by an algorithm, or when the [[class]] internal property doesn't have the expected value. The Array.prototype.reduce* functions throw it when a required argument is missing, and the RegExp constructor when an unwanted argument is present. JSON.stringify throws it when given a cyclic structure.

  • ValueError - as introduced in the Globalization API spec.

ValueError would be unnecessary if the definition of RangeError or TypeError were expanded to cover string value mismatches as well.

Which way should we go?

Just use Error and focus you energy on creating useful content in the error object.

jjb

# Brendan Eich (14 years ago)

On Nov 22, 2011, at 9:37 AM, Allen Wirfs-Brock wrote:

Range error actually has fairly limited usage in the current specification. Enough so, that it arguably doesn't carry its weight as one of the very few available exceptions "classes". I think we could reasonably drop the "numeric" from its prose description and use it for all (new) situations where a provided value is of the correct "type" but outside the set of acceptable values of that type.

Pedants and wannabes like me will object to "Range" instead of "Value" (codomain vs. domain :-P). But agreed, we could use RangeError in more places; we could do worse.

Is there really any situation where it would make sense for an exception handler to filter for numeric range error but would exclude non-numeric range errors? I find it implausible as ES exceptions just aren't that precisely identified.

Yup.

Unfortunately, as it now stands you have to use the implementation dependent message property value to actually identify specific exceptions.

*Error.prototype.name is normatively specified for all the built-in Error constructors.

I wonder if for new exception occurrences (in our specs.) we should specify unique implementation independent name property values. To do so, we would have to develop a meaningful fine-grained naming scheme.

This would fit in just Error, as JJB just suggested.

Folks who were around for ES3 should weigh in, in case we're missing something that can be improved in the *Error design originated in that spec. IIRC it was from TC39 TG1, design by committee or a champion on that task group. It was not from implementation-led de-facto standards.

# Allen Wirfs-Brock (14 years ago)

On Nov 22, 2011, at 10:14 AM, Brendan Eich wrote:

On Nov 22, 2011, at 9:37 AM, Allen Wirfs-Brock wrote: ...

Unfortunately, as it now stands you have to use the implementation dependent message property value to actually identify specific exceptions.

*Error.prototype.name is normatively specified for all the built-in Error constructors.

Yes, but name doesn't discriminate the actual cause of the exception. There is no normative way to distinguish a RangeError generated by 1.0.toPrecision(40) from a RangeError generated by (new Array(1.2))

I wonder if for new exception occurrences (in our specs.) we should specify unique implementation independent name property values. To do so, we would have to develop a meaningful fine-grained naming scheme.

This would fit in just Error, as JJB just suggested.

The difference is that instanceof can be used to detect RageError, TypeError, etc. instances

# Brendan Eich (14 years ago)

On Nov 22, 2011, at 10:49 AM, Allen Wirfs-Brock wrote:

On Nov 22, 2011, at 10:14 AM, Brendan Eich wrote:

On Nov 22, 2011, at 9:37 AM, Allen Wirfs-Brock wrote: ...

Unfortunately, as it now stands you have to use the implementation dependent message property value to actually identify specific exceptions.

*Error.prototype.name is normatively specified for all the built-in Error constructors.

Yes, but name doesn't discriminate the actual cause of the exception. There is no normative way to distinguish a RangeError generated by 1.0.toPrecision(40) from a RangeError generated by (new Array(1.2))

I see -- but do we really want name variations for all of those? Where does the offending value go, if anywhere?

I wonder if for new exception occurrences (in our specs.) we should specify unique implementation independent name property values. To do so, we would have to develop a meaningful fine-grained naming scheme.

This would fit in just Error, as JJB just suggested.

The difference is that instanceof can be used to detect RageError, TypeError, etc. instances

Right, instanceof. Kind of a flop, especially in a mutliple-globals embedding.

# John J Barton (14 years ago)

On Tue, Nov 22, 2011 at 10:49 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

On Nov 22, 2011, at 10:14 AM, Brendan Eich wrote:

On Nov 22, 2011, at 9:37 AM, Allen Wirfs-Brock wrote: ...

Unfortunately, as it now stands  you have to use the implementation dependent message property value to actually identify specific exceptions.

*Error.prototype.name is normatively specified for all the built-in Error constructors.

Yes, but name doesn't discriminate the actual cause of the exception.

"actual cause of" can mean different things. I guess here you mean "the particular failure path described in the standard for the function that triggered". The unique error path in the specification could be given an unique name and that unique name could be encoded in the type name or .name field or the toString(). I think this would be attractive to implementers simply because it would give concrete instructions for these cases. The unique names would be helpful for devs, a search would lead to the standard spec if nothing else and can be useful keys for docs.

I guess a |name| field and spec that says it should appear somewhere in the toString() has the best chance for adoption. Typenames are more overhead. Go with the simplest solution that can get the most info into the Error object.

There is no normative way to distinguish a RangeError generated by 1.0.toPrecision(40) from a RangeError generated by (new Array(1.2))

A great way distinguish these cases is to include the text of the source that triggered the exception, eg "RangeError at (new Array(1.2))". Unfortunately I don't think browsers will agree to implement this.

I wonder if for new exception occurrences (in our specs.) we should specify unique implementation independent name property values. To do so, we would have to develop a meaningful fine-grained naming scheme.

This would fit in just Error, as JJB just suggested.

The difference is that instanceof can be used to detect RageError, TypeError, etc. instances

This has limited use because we can't magically cause all exceptions to be correctly typed and because exceptions can jump across iframes and the instanceof test only works within a single iframe.

(BTW we really need ChainedError in JS).

jjb

# Norbert Lindenberg (14 years ago)

Sounds like RangeError is the one to use for the Globalization API. If I don't hear objections, I'll remove ValueError from the spec, replace its uses with RangeError, and file a ticket against the language specification to have the word "numeric" removed from the description of RangeError.

Thanks, Norbert

# David Herman (14 years ago)

On Nov 22, 2011, at 10:14 AM, Brendan Eich wrote:

On Nov 22, 2011, at 9:37 AM, Allen Wirfs-Brock wrote:

Range error actually has fairly limited usage in the current specification. Enough so, that it arguably doesn't carry its weight as one of the very few available exceptions "classes". I think we could reasonably drop the "numeric" from its prose description and use it for all (new) situations where a provided value is of the correct "type" but outside the set of acceptable values of that type.

Pedants and wannabes like me will object to "Range" instead of "Value" (codomain vs. domain :-P). But agreed, we could use RangeError in more places; we could do worse.

And then pedants like me will object to your interpretation of "range" in this context. :) The way "range" is being used is as an "interval", which is common in CS, and not the same as the mathematical tradition. (In fact, there are multiple conflicting uses of the word "range" in math.)

An interval implies an ordering. It can be specified by lower and upper bounds. Using RangeError for "does not fall in the expected set of values" stretches the meaning of "range" beyond its normal meaning, which is "does not fall in the expected interval of values."

TypeError is a much more appropriate error here. "This value does not fall in the expected set of values" is the way TypeError is used pervasively in JS.

# David Herman (14 years ago)

On Nov 28, 2011, at 12:30 PM, Norbert Lindenberg wrote:

Sounds like RangeError is the one to use for the Globalization API. If I don't hear objections, I'll remove ValueError from the spec, replace its uses with RangeError, and file a ticket against the language specification to have the word "numeric" removed from the description of RangeError.

RangeError is the wrong choice here. TypeError fits within JS tradition. I agree that ValueError is redundant, but it should be replaced by TypeError rather than RangeError. The intuition of a type as describing a "set of expected values" tends to work well for JS.

# Allen Wirfs-Brock (14 years ago)

On Nov 30, 2011, at 9:06 AM, David Herman wrote:

On Nov 22, 2011, at 10:14 AM, Brendan Eich wrote:

On Nov 22, 2011, at 9:37 AM, Allen Wirfs-Brock wrote:

Range error actually has fairly limited usage in the current specification. Enough so, that it arguably doesn't carry its weight as one of the very few available exceptions "classes". I think we could reasonably drop the "numeric" from its prose description and use it for all (new) situations where a provided value is of the correct "type" but outside the set of acceptable values of that type.

Pedants and wannabes like me will object to "Range" instead of "Value" (codomain vs. domain :-P). But agreed, we could use RangeError in more places; we could do worse.

And then pedants like me will object to your interpretation of "range" in this context. :) The way "range" is being used is as an "interval", which is common in CS, and not the same as the mathematical tradition. (In fact, there are multiple conflicting uses of the word "range" in math.)

Except that ES doesn't even consistently follow that definition. For example:

15.4.2.2: If the argument len is a Number and ToUint32(len) is equal to len, then the length property of the newly constructed object is set to ToUint32(len). If the argument len is a Number and ToUint32(len) is not equal to len, a RangeError exception is thrown.

new Array(1.2) RangeError on line 1: invalid array length

# David Herman (14 years ago)

On Nov 29, 2011, at 2:34 PM, Allen Wirfs-Brock wrote:

On Nov 30, 2011, at 9:06 AM, David Herman wrote:

And then pedants like me will object to your interpretation of "range" in this context. :) The way "range" is being used is as an "interval", which is common in CS, and not the same as the mathematical tradition. (In fact, there are multiple conflicting uses of the word "range" in math.)

Except that ES doesn't even consistently follow that definition. For example:

15.4.2.2: If the argument len is a Number and ToUint32(len) is equal to len, then the length property of the newly constructed object is set to ToUint32(len). If the argument len is a Number and ToUint32(len) is not equal to len, a RangeError exception is thrown.

new Array(1.2) RangeError on line 1: invalid array length

You could certainly argue that that should have been a TypeError, but the expected values are integers in the range [0, 2^32). There's still a reasonable intuition that "range" means "numeric interval". Why redefine RangeError to mean TypeError when we already have TypeError?

# John J Barton (14 years ago)

On Tue, Nov 29, 2011 at 8:38 PM, David Herman <dherman at mozilla.com> wrote:

On Nov 29, 2011, at 2:34 PM, Allen Wirfs-Brock wrote:

On Nov 30, 2011, at 9:06 AM, David Herman wrote:

And then pedants like me will object to your interpretation of "range" in this context. :) The way "range" is being used is as an "interval", which is common in CS, and not the same as the mathematical tradition. (In fact, there are multiple conflicting uses of the word "range" in math.)

Except that ES doesn't even consistently follow that definition.  For example: 15.4.2.2: If the argument len is a Number and ToUint32(len) is equal to len, then the length property of the newly constructed object is set to ToUint32(len). If the argument len is a Number and ToUint32(len) is not equal to len, a RangeError exception is thrown. new Array(1.2) RangeError on line 1: invalid array length

You could certainly argue that that should have been a TypeError, but the expected values are integers in the range [0, 2^32). There's still a reasonable intuition that "range" means "numeric interval". Why redefine RangeError to mean TypeError when we already have TypeError?

Why discuss any of these when the important and useful information is in the Error object content? Save some paint for the other side of the shed ;-)

jjb