Error vs Exception? Why both? When to use which? When to subclass which?

3,176 views
Skip to first unread message

Kevin Moore

unread,
Mar 22, 2013, 12:22:34 PM3/22/13
to General Dart Discussion
I'm probably not the only person who lazily writes:

throw "don't do that!";

But when we're trying to write "good idiomatic" Dart, what's the best practice?

The docs on Exception say one should subclass Exception, but there are also a number of subclasses of Error.

Should Error subclass Exception or vice-versa? If we create a custom exception/error class should it always subclass? Which one when?

Etc...

>Kevin

Andrei Mouravski

unread,
Mar 22, 2013, 12:27:20 PM3/22/13
to General Dart Discussion
I too have this question... I recommend putting this question (and whatever the right answer is) on StackOverflow so it's visible to everyone who does "throw 'foo'' like we both do. :] 


--
Consider asking HOWTO questions at Stack Overflow: http://stackoverflow.com/tags/dart
 
 

James Wendel

unread,
Mar 22, 2013, 12:43:26 PM3/22/13
to mi...@dartlang.org
I believe this follows the thought process similar to Java.

Exceptions should be used when there is a problem that is expected.  A common one is any type of I/O operation (like network traffic), where the socket closes early, and trying to write data to that socket fails.

Errors occur when there is a problem that was not expected.  Things like null pointers (you expected this variable to not be null), running our of memory, etc...

For the most part you, as an app developer, will always use exceptions.  Errors tend to be reserved for unexpected and fatal problems.

Ladislav Thon

unread,
Mar 22, 2013, 12:43:38 PM3/22/13
to mi...@dartlang.org
I too have this question... I recommend putting this question (and whatever the right answer is) on StackOverflow so it's visible to everyone who does "throw 'foo'' like we both do. :] 

I believe that the convention is similar to Java's distinction between checked and unchecked (runtime) exceptions. In Dart, exceptions represent a situation that can happen, but isn't usually expected (i.e., "exceptional" situation). Like trying to open a file that doesn't exist. Error represents a situation that shouldn't happen and is definitely programmer's fault. Like trying to divide by zero.

LT

Bob Nystrom

unread,
Mar 22, 2013, 12:54:29 PM3/22/13
to General Dart Discussion

On Fri, Mar 22, 2013 at 9:43 AM, Ladislav Thon <lad...@gmail.com> wrote:
I believe that the convention is similar to Java's distinction between checked and unchecked (runtime) exceptions. In Dart, exceptions represent a situation that can happen, but isn't usually expected (i.e., "exceptional" situation). Like trying to open a file that doesn't exist. Error represents a situation that shouldn't happen and is definitely programmer's fault. Like trying to divide by zero.

This is exactly right.

Error and its subclasses are for programmatic errors. If one of those occurs, your code is bad and you should fix your code.

Non-Error exception classes are for runtime errors. Sometimes you can prevent them from being thrown, but often you cannot.

Except in a few special circumstances, idiomatic Dart should throw Errors, but never catch them. They exists specifically to not be caught so that they take down the app and alert the programmer to the location of the bug.

Cheers!

- bob

Kevin Moore

unread,
Mar 22, 2013, 1:02:22 PM3/22/13
to mi...@dartlang.org
OutOfMemoryError, OSError, StackOverflowError -- don't align well with "definitely programmer's fault".

(Stack and Memory errors could be the programmers fault, if they have un-bounded recursion, but that's besides the point.)

I like the idea that there are two buckets.

Bucket A: things that happen that a perfect programmer couldn't avoid even if they tried. Network, Disk, OS, Memory. Anything that steps outside the "environment".

Bucket B: all the rest.

Questions
  1. Does this catch everything? Make sense? (Forgetting the actual names for a second...)
  2. Seems like we're saying Bucket A is Exceptions and Bucket B is Errors, right?
  3. Seems like the examples above (OS, StackOverflow, OOM) probably align with Bucket B (exception?)
  4. Assuming 1-3
    1. New issue on aligning existing values w/ the correct philosophy
    2. New issue to document said philosophy
Cool?

Kevin Moore

unread,
Mar 22, 2013, 1:11:37 PM3/22/13
to mi...@dartlang.org
Crap. My #3 is wrong. Those should be Bucket A - Exceptions.

See. This is hard.

Alex Tatumizer

unread,
Mar 22, 2013, 1:15:29 PM3/22/13
to mi...@dartlang.org
@Bob: Did you ever discuss runtime assert? The one that doesn't get removed in unchecked mode?
The lack of lightweight method for a programmer to check himself creates a disincentive.
E.g., if I know at some point of the program i should be greater than j, I have a choice:
1) write if (i<=j) throw "busted! i<=j!!!"
2) create my own convenience method check(cond, [message])
3) do nothing

1) hurts readability, 2) is a hassle (where to declare it? in each module?), so I go with 3)
BTW, message in these situations is not needed at all, condition is enough


Lasse R.H. Nielsen

unread,
Mar 22, 2013, 1:20:51 PM3/22/13
to mi...@dartlang.org
The distinction is more about whether the user can meaningfully do something about the problem if he catches the thrown object.

For programming Errors, we generally don't want users to catch them - they should fix the bug instead, or get the library fixed. Catching it is at best a workaround. The errors are pretty generic, and are only intended to help with debugging.

For resource exhaustion Errors (OOM, stack overflow), it will usually be caused by an error (keeping too much crap alive, unbounded recursion). Sure you can catch them - for example to do recursion as long as possible, then unwind and start again, but it's not something we want to encourage. It is intended to say that the program won't run on this setup, and then let it die.

Exceptions, on the other hand, can be treated more like an alternative return type. The exception will likely be domain specific, and may carry information that can be used by the caller to do something about the problem. If you want to throw a generic Exception, consider whether it should really be an Error.

/L
--
Lasse R.H. Nielsen - l...@google.com  
'Faith without judgement merely degrades the spirit divine'
Google Denmark ApS - Frederiksborggade 20B, 1 sal - 1360 København K - Denmark - CVR nr. 28 86 69 84

Ladislav Thon

unread,
Mar 22, 2013, 1:38:04 PM3/22/13
to mi...@dartlang.org
OutOfMemoryError, OSError, StackOverflowError -- don't align well with "definitely programmer's fault".

OOM or stack overflow definitely are programmer's faults, even though they are a bit different from the usual ones.

OSError, well, I don't really know that, but looking at the source, it looks like it's only used internally inside dart:io. I'd like to get a comment on whether dart:io user can ever get that exception.

LT
Message has been deleted

Kevin Moore

unread,
Mar 22, 2013, 2:01:00 PM3/22/13
to mi...@dartlang.org
Here's where things get crazy, though.

A poorly designed system could certainly OOM or SOF, but there is no way to know, as a programmer if a call XYZ method will cause such a state.

Also, depending on the environment, the exact same code could OOM/SOF in one context and not another.

I'd claim OOM/SOF are different beasts than null argument or index out of bounds or divid by zero.

--

Kevin Moore

unread,
Mar 22, 2013, 2:14:06 PM3/22/13
to mi...@dartlang.org
Exactly.


To minimize further bike shedding, I'd ask folks in the Lang Design group (or similar) take this up and report back.

I could imagine a dozen ways to slice this. As long as we end up with conscientious, clarity, and consistency, I'll be happy. 

On Friday, March 22, 2013 2:00:45 PM UTC-4, Ross Smith wrote:
> Except in a few special circumstances, idiomatic Dart should throw Errors, but never catch them. 

If we should generally only catch Exceptions, and not Errors, why does Future have a method named _catchError_ instead of _catchException_ ?

Kevin Moore

unread,
Mar 22, 2013, 2:16:02 PM3/22/13
to mi...@dartlang.org
...and also consensus, although being conscientious about the process is appreciated. :-)

Bob Nystrom

unread,
Mar 22, 2013, 4:03:18 PM3/22/13
to General Dart Discussion
On Fri, Mar 22, 2013 at 10:02 AM, Kevin Moore <kev...@j832.com> wrote:
OutOfMemoryError, OSError, StackOverflowError -- don't align well with "definitely programmer's fault".

(Stack and Memory errors could be the programmers fault, if they have un-bounded recursion, but that's besides the point.)

There's actually three buckets, but I left out the third for simplicity's sake. The third bucket is system errors. These are errors where the underlying Dart implementation can no longer preserve the idealized semantics that the language specifies. For example, the Dart spec says [] returns a new list. When you're out of memory, the implementation can no longer follow that part of the spec, so it throws a system error.

These are similar to programmatic errors in that you shouldn't catch them. Instead, prevent them when you can.
 

I like the idea that there are two buckets.

Bucket A: things that happen that a perfect programmer couldn't avoid even if they tried. Network, Disk, OS, Memory. Anything that steps outside the "environment".

Bucket B: all the rest.

Questions
  1. Does this catch everything? Make sense? (Forgetting the actual names for a second...)
  2. Seems like we're saying Bucket A is Exceptions and Bucket B is Errors, right?
  3. Seems like the examples above (OS, StackOverflow, OOM) probably align with Bucket B (exception?)
  4. Assuming 1-3
    1. New issue on aligning existing values w/ the correct philosophy
    2. New issue to document said philosophy
The other thing in bucket A is errors that could be prevented at runtime but where doing so requires doing just as much work as performing the operation that fails. For example, you could prevent int.parse() from throwing a FormatException by first validating that the string can be successfully parsed. But doing so takes just as much effort as parsing the string in the first place.

For me, the three conceptual buckets are programmatic error (ArgumentError, StateError, RangeError), system error (OutOfMemoryError, StackOverflowError), and runtime error (IOException, FormatException).

Pragmatically, system and programmatic errors are used the same way: you don't catch them but you're aware they may be thrown. You try to prevent them from happening programmatically.

So my two effective buckets are:

1. Exception: stuff you catch: runtime errors.
2. Error: stuff you don't catch: system and programmatic errors.

- bob

John Messerly

unread,
Mar 22, 2013, 4:16:08 PM3/22/13
to General Dart Discussion
On Fri, Mar 22, 2013 at 1:03 PM, Bob Nystrom <rnys...@google.com> wrote:
2. Error: stuff you don't catch: system and programmatic errors.

Except in tests :). Of course, you probably aren't catching it directly, but using matchers:

    expect(() => thing, throwsA(new isInstanceOf<ArgumentError>()));

(unrelated observation: that's kinda verbose)

Bob Nystrom

unread,
Mar 22, 2013, 4:19:19 PM3/22/13
to General Dart Discussion
On Fri, Mar 22, 2013 at 1:16 PM, John Messerly <jmes...@google.com> wrote:
Except in tests :). Of course, you probably aren't catching it directly, but using matchers:

That's why I said "Except in a few special circumstances" earlier. :)

The other common case is top-level exception handlers that catch everything so they can log it.
 

    expect(() => thing, throwsA(new isInstanceOf<ArgumentError>()));

(unrelated observation: that's kinda verbose)

expect(() => thing, throwsArgumentError);

:)

- bob

Alex Tatumizer

unread,
Mar 22, 2013, 4:25:44 PM3/22/13
to mi...@dartlang.org
> 1. Exception: stuff you catch: runtime errors.
> 2. Error: stuff you don't catch: system and programmatic errors.
This definition implies (by the very way it's worded) that it depends on specific app (after all, it your app that catches exceptions, or it doesn't).
In the application, you make decision to catch this kind of error, or not. In another application, you can treat same error differently.
Zerodivide might be innocuous (e.g. if your app is calculator). File Not Found may be your bug.
Even out-of-memory can be recoverable in certain situations (unless dart runtime makes it impossible in principle).




--

Alex Tatumizer

unread,
Mar 22, 2013, 4:39:00 PM3/22/13
to mi...@dartlang.org
OK, simple question:
I'm writing a lib, and I'm going to throw something. Should it be Error or Exception?
Turns out, it depends on whether application is going to catch it, or not.
How can I know it? Which application? I'm not writing application, it's a lib!

So, now I have to ask myself: is it true that for every application it will be a critical error?
The answer to this question is NO. I cannot be sure about EVERY application.

This separation creates a hard problem without tenable solution.

John Messerly

unread,
Mar 22, 2013, 4:39:54 PM3/22/13
to General Dart Discussion
Awesome! Not sure when that appeared, but regardless it is awesome. brb fixing all the codes!

John Messerly

unread,
Mar 22, 2013, 4:43:36 PM3/22/13
to General Dart Discussion
Alex, I think you're over thinking this.

If someone is using your API's incorrectly -- throw an Error.
Anything else -- throw an Exception.

In practice, the errors you'll use are likely to be ArgumentError, StateError, UnsupportedError, and UnimplementedError. A few others like RangeError and ConcurrentModificationError will come into play if implementing list-like types or iterators.

Otherwise make it an Exception.

Easy!



Alex Tatumizer

unread,
Mar 22, 2013, 4:56:22 PM3/22/13
to mi...@dartlang.org
@John: your response makes sense to me.
It would be good if it were documented like this somewhere.
Thanks.


Allan MacDonald

unread,
Mar 23, 2013, 4:38:35 AM3/23/13
to General Dart Discussion

I think in terms of these two buckets as well. Another way to think about them are;


>
> So my two effective buckets are:
>
> 1. Exception: stuff you catch: runtime errors.

Things that are recoverable

> 2. Error: stuff you don't catch: system and programmatic errors.

Things that are not recoverable.

Greg Lowe

unread,
Mar 23, 2013, 7:14:44 PM3/23/13
to mi...@dartlang.org
Based on the discussion above, in my opinion, it would might make sense to rename the exception, and error classes.

If I were the language dictator...

  Exception => Error
  Error => Fault

So RangeError => RangeFault etc.
And FormatException => FormatError etc.

The advantage of this is that the naming matches better with future.catchError().

The disadvantage is that it's less familiar to Java programmers.

Maybe it's just me - I dislike the word exception, and I often see the word error and exception used interchangeably in documentation.

Alex Tatumizer

unread,
Mar 23, 2013, 7:53:25 PM3/23/13
to mi...@dartlang.org
If Error is defined as something that makes it impossible to continue (e.g. there's suspicion that the runtime state is corrupted beyond repair),
then won't it be better to use different mechanism whatsoever?
E.g. panic() or abort() on such errors.
There can be a way to intercept even those events, e.g. to log some message, but "return" from this interceptor will cause shutdown anyway.
I mean, if the difference is so clear-cut, then why not make it even clear-cutter?
 



Greg Lowe

unread,
Mar 23, 2013, 9:04:27 PM3/23/13
to mi...@dartlang.org

use different mechanism

Same mechanism seems fine to me. You still need to be able to catch Faults sometimes. (erm Errors - I like my new language already...)
 
 

Alex Tatumizer

unread,
Mar 24, 2013, 9:56:56 AM3/24/13
to mi...@dartlang.org
Good luck catching these Faults to exit gracefully.
You will have to include them in every future.onError() logic.
If you have 100 futures - will handle them in 100 places. 
LOL.





Kevin Moore

unread,
Jan 18, 2014, 3:59:11 PM1/18/14
to mi...@dartlang.org
FYI: Error and Exception have both shipped in V1, so they are likely to stick around for a while.

Opened an issue to track documenting Error https://code.google.com/p/dart/issues/detail?id=16192

The docs on Exception do a good job of explaining usage.

Alex Tatumizer

unread,
Jan 19, 2014, 12:21:43 AM1/19/14
to mi...@dartlang.org
After Zones were added to async library, my complaint about 100 places is not valid any longer - apparently, now you  can catch all errors in a single place via "unhandled error" callback.
(please correct me if I'm wrong).
Details are sketchy though - e.g. whether the term "unhandled error" refers specifically to errors only, or catches exceptions, too, or what?





--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new

Reply all
Reply to author
Forward
0 new messages