Exception type for "invalid operations"?

# Domenic Denicola (6 years ago)

I'm trying to design the whatwg/streams spec in the style of ECMAScript primitives, since I find that style more precise and idiomatic, and potentially in the future streams could become a language-level feature. Basically, I want to get ahead of the situation TextEncoder/TextDecoder find themselves in, as per recent discussions.

One thing I'm stuck on is what exception type to use for invalid operations. For example, trying to read from or write to a closed stream. None of the ECMAScript standard types---EvalError, RangeError, ReferenceError, SyntaxError, TypeError, and URIError---seem to match. Do I just give up and use TypeError, which seems to be the catch-all in most situations?

Or would it make sense to open ourselves up beyond the existing set, and define some kind of InvalidOperationError? The idea being that, if streams were to become an ECMAScript primitive, so would InvalidOperationError. (If, not when! Please don't read too much presumptuousness into my API design predilections.)

Other languages seem to have something similar: .NET's InvalidOperationException and Java's IllegalStateException come to mind. But of course they have much deeper exception hierarchies, which I don't think we want to emulate.

# Allen Wirfs-Brock (6 years ago)

So far we've avoiding adding any new built-in exceptions. Perhaps, we could add another but I think it would take some time to build consensus around that.

In practice new library functions in ES6 restrict themselves to using either TypeError or RangeError with TypeError being by far the most common. Range error is used when a parameter or other value is (likely) of the correct primitive type (number, string, etc.) but the specific value is not contextually valid. Pretty much everything else is treated as a TypeError. Note that in this context we interpret "Type" more like "kind" than corresponding the ECMAScript types. You can think about this usage of TypeError as "the wrong kind of thing was directly or indirectly passed to the function". If you invert things you can think of "invalid operation" as trying to apply a valid operation upon the "wrong kind of object" so TypeError is a plausible.

In practice, where would throwing OperationError instead of TypeError actually make any difference. How would you handle one differently from the other?

# Domenic Denicola (6 years ago)

From: Allen Wirfs-Brock <allen at wirfs-brock.com>

If you invert things you can think of "invalid operation" as trying to apply a valid operation upon the "wrong kind of object" so TypeError is a plausible.

This seems like a stretch :P. Are there existing instances in the ES spec of this kind of use? I guess maybe trying to write to a non-writable property in strict mode and the like.

In practice, where would throwing OperationError instead of TypeError actually make any difference. How would you handle one differently from the other?

In practice it makes almost no difference, just like all other ECMAScript error type discrimination. It just seems that when a user sees TypeError in a stack trace, they expect to find some kind of argument validation problem.

(Maybe once we get pattern-matching catch clauses it would matter more. E.g. people could purposefully try to catch OperationErrors from such operations, and handle them differently, leaving TypeErrors etc. to bubble outward and hit a top-level exception handler as "truly unexpected." But that's a ways off.)


Sounds like TypeError is probably the way to go, even if it feels a bit off.

# Allen Wirfs-Brock (6 years ago)

On Jan 30, 2014, at 8:49 AM, Domenic Denicola wrote:

Are there existing instances in the ES spec of this kind of use? I guess maybe trying to write to a non-writable property in strict mode and the like.

Sure, all over the place. For example, trying to apply a built-in constructor to an object that doesn't have the expected set of internal slots.

Sounds like TypeError is probably the way to go, even if it feels a bit off.

We work with the tools that are available...

# Joshua Bell (6 years ago)

On Thu, Jan 30, 2014 at 8:25 AM, Domenic Denicola < domenic at domenicdenicola.com> wrote:

I'm trying to design the [whatwg/streams spec][1] in the style of ECMAScript primitives, since I find that style more precise and idiomatic, and potentially in the future streams could become a language-level feature. Basically, I want to get ahead of the situation TextEncoder/TextDecoder find themselves in, as per [recent discussions][2].

Even following the link, I'm unsure if you're referring to the use of DOMException with name "EncodingError", or something else in that thread. (If something else, can you ping me directly?)

One thing I'm stuck on is what exception type to use for invalid operations. For example, trying to read from or write to a closed stream. None of the ECMAScript standard types---EvalError, RangeError, ReferenceError, SyntaxError, TypeError, and URIError---seem to match. Do I just give up and use TypeError, which seems to be the catch-all in most situations?

Distinct DOMException types used to be defined with a numeric enum. That's changed to distinct strings (hence the DOMException w/ name "EncodingError", for example), and specs can add new types without requiring additions to an enum tracked in the DOM spec.

Even with the ability to have feature-specific names error names, the specific types don't communicate enough detail for developers to know what's going on (e.g. "InvalidStateError" - uh, why?). In Chromium we're trying to update the messages to indicate e.g. the method being called ("Failed to execute 'put' on 'IDBObjectStore': ...") in addition to the specific details ("The transaction is not active."). But at least with the flexibility for a feature to define a specific error, you can point to the line in the spec that explains why it's being thrown. Which argues for allowing feature-specific error types and against an explosion of types that need defining in ES where strings would be sufficient.

So... can we simply use Error() with documented |name| (for code to reason about) and detailed |message| (for developers to log/debug) ?