110

My specific case here is that the user can pass in a string into the application, the application parses it and assigns it to structured objects. Sometimes the user may type in something invalid. For example, their input may describe a person but they may say their age is "apple". Correct behavior in that case is roll back the transaction and to tell the user an error occurred and they'll have to try again. There may be a requirement to report on every error we can find in the input, not just the first.

In this case, I argued we should throw an exception. He disagreed, saying, "Exceptions should be exceptional: It's expected that the user may input invalid data, so this isn't an exceptional case" I didn't really know how to argue that point, because by definition of the word, he seems to be right.

But, it's my understanding that this is why Exceptions were invented in the first place. It used to be you had to inspect the result to see if an error occurred. If you failed to check, bad things could happen without you noticing.

Without exceptions every level of the stack needs to check the result of the methods they call and if a programmer forgets to check in one of these levels, the code could accidentally proceed and save invalid data (for example). Seems more error prone that way.

Anyway, feel free to correct anything I've said here. My main question is if someone says Exceptions should be exceptional, how do I know if my case is exceptional?

11
  • 4
    possible duplicate? When to throw an exception. Though it was closed there, but I think it fits here. It's still a bit of philosophy, some people and communities tend to see exceptions as a kind of flow control. Commented Jan 24, 2013 at 8:58
  • 8
    When users are dumb, they provide invalid input. When users are smart, they play by providing invalid input. Therefore, invalid user input is not an exception.
    – mouviciel
    Commented Jan 24, 2013 at 12:10
  • 7
    Also, don't confuse an exception, which is a very specific mechanism in Java and .NET, with an error which is a much more generic term. There's more to error handling than throwing exceptions. This discussion touches on the nuances between exceptions and errors.
    – Eric King
    Commented Jan 24, 2013 at 14:39
  • 4
    "Exceptional" != "Rarely Happens" Commented Jan 24, 2013 at 17:30
  • 3
    I find Eric Lippert's Vexing exceptions to be decent advice.
    – Brian
    Commented Nov 19, 2013 at 14:02

12 Answers 12

95

Exceptions were invented to help make error handling easier with less code clutter. You should use them in cases when they make error handling easier with less code clutter. This "exceptions only for exceptional circumstances" business stems from a time when exception handling was deemed an unacceptable performance hit. That's no longer the case in the vast majority of code, but people still spout the rule without remembering the reason behind it.

Especially in Java, which is maybe the most exception-loving language ever conceived, you shouldn't feel bad about using exceptions when it simplifies your code. In fact, Java's own Integer class doesn't have a means to check if a string is a valid integer without potentially throwing a NumberFormatException.

Also, although you can't rely just on UI validation, keep in mind if your UI is designed properly, such as using a spinner for entering short numerical values, then a non-numerical value making it into the back end truly would be an exceptional condition.

12
  • 11
    Nice swipe, there. Actually, in the real-world app I designed, the performance hit did make a difference, and I had to change it to not throw exceptions for certain parsing operations. Commented Jan 24, 2013 at 17:22
  • 20
    I'm not saying there aren't still cases where the performance hit is a valid reason, but those cases are the exception (pun intended) rather than the rule. Commented Jan 24, 2013 at 17:38
  • 12
    @RobertHarvey The trick in Java is to throw pre-fabricated exception objects, rather than throw new .... Or, throw custom exceptions, where fillInStackTrace() is overwritten. Then you shouldn't notice any performance degradations, not to speak of hits.
    – Ingo
    Commented Jan 24, 2013 at 21:42
  • 4
    +1: Exactly what I was going to answer. Use it when it simplifies the code. Exceptions can give much clearer code where you do not have to bother checking return values on every level in the call-stack. (Like everything else though, if used the wrong way it can make your code a horrible mess.)
    – Leo
    Commented Jan 25, 2013 at 7:58
  • 6
    @Brendan Suppose some exception occurs, and the error-handling code is 4 levels below in the call stack. If you use an error code, all 4 functions above the handler need to have the error code's type as their return value, and you have to do a chain of if (foo() == ERROR) { return ERROR; } else { // continue } at every level. If you throw an unchecked exception, there's no noisy and redundant "if error return error". Also, if you're passing functions as arguments, using an error code may change the function signature to an incompatible type, even though the error may not occur.
    – Doval
    Commented Mar 25, 2014 at 19:52
76

When should an exception be thrown? When it comes to code, I think that following explanation is very helpful:

An exception is when a member fails to complete the task it is supposed to perform as indicated by its name. (Jeffry Richter, CLR via C#)

Why is it helpful? It suggests that it depends on the context when something should be handled as an exception or not. On the level of method calls, the context is given by (a) the name, (b) signature of the method and (b) the client code, which uses or is expected to use the method.

To answer your question you should have a look on the code, where user input is processed. It might look something like this:

public void Save(PersonData personData) { … }

Does the method name suggest that some validation is done? No. In this case an invalid PersonData should throw an exception.

Suppose that the class has another method which looks like this:

public ValidationResult Validate(PersonData personData) { … }

Does the method name suggest that some validation is done? Yes. In this case an invalid PersonData should not throw an exception.

To put the things together, both methods suggest that the client code should look like this:

ValidationResult validationResult = personRegister.Validate(personData);
if (validationResult.IsValid())
{
    personRegister.Save(personData)
}
else
{
    // Throw an exception? To answer this look at the context!
    // That is: (a) Method name, (b) signature and
    // (c) where this method is (expected) to be used.
}

When it is not clear if a method should throw an exception, then maybe it is due a poorly chosen method name or signature. Maybe the design of the class is not clear. Sometimes you need to modify the code design to get a clear answer to the question if an exception should be thrown or not.

9
  • Just yesterday I made a struct called "ValidationResult" and structured my code the way you describe.
    – paul
    Commented Jan 24, 2013 at 13:14
  • 4
    It does not help to answer your question, but I just like to point out that you implicitly or purposely followed the Command-query separation principle (en.wikipedia.org/wiki/Command-query_separation). ;-) Commented Jan 24, 2013 at 13:21
  • Nice idea! One drawback: In your example, validation is actually performed twice: Once during Validate (returning False if invalid) and once during Save (throwing a specific, well-documented exception if invalid). Of course, the validation result could be cached inside the object, but that would add additional complexity, since the validation result would need to be invalidated on changes.
    – Heinzi
    Commented Oct 21, 2013 at 9:27
  • 1
    Another problem with this approach is that it can only be used if you are sure that there will be no interference from other threads or processes that might affect the validation result. By the time you call Save, the personData may no longer be valid.
    – flodin
    Commented Dec 16, 2014 at 10:36
  • 3
    This is better than the accepted answer i think. Throw when the call can't do the thing it was supposed to do.
    – Andy
    Commented Oct 29, 2015 at 1:32
34

Exceptions should be exceptional: It's expected that the user may input invalid data, so this isn't an exceptional case

On that argument:

  • It's expected that a file may not exist, so that isn't an exceptional case.
  • It's expected that the connection to the server may be lost, so that isn't an exceptional case
  • It's expected that the configuration file may be garbled so that isn't an exceptional case
  • It's expected that your request may sometimes fall out, so that isn't an exceptional case

Any exception that you catch, you must expect because, well, you decided to catch it. And so by this logic, you should never throw an any exceptions you actually plan to catch.

Hence I think "exceptions should be exceptional" is a terrible rule of thumb.

What you should do depends on the language. Different languages have different conventions about when exceptions should be thrown. Python, for example, throws exceptions for everything and when in Python, I follow suit. C++, on the other hand, throws relatively few exceptions, and there I follow suit. You can treat C++ or Java like Python and throw exceptions for everything, but your working at odds with how the language expects itself to be used.

I prefer Python's approach, but I think it a bad idea to shoehorn other languages into it.

14
  • 1
    @gnat, I know. My point was that you should follow the conventions of the language (in this case Java) even if they aren't your favorite. Commented Jan 24, 2013 at 15:18
  • 8
    +1 "exceptions should be exceptional" is a terrible rule of thumb. Well said! That's one of those things people just repeat without thinking about them.
    – Andres F.
    Commented Jan 24, 2013 at 22:51
  • 2
    "Expected" is defined by not by subjective argument or convention but by the contract of the API/function (this might be explicit, but is often just implied). Different functions / APIs / subsystems can have different expectations, e.g. for some higher level functionality a non-existing file is an expected case for it to handle (it might report this to a user via a GUI), for other lower level functions it is probably not (and hence should throw an exception). This answer seems to miss that important point....
    – mikera
    Commented Oct 21, 2013 at 6:05
  • 1
    @mikera, yes a function should (only) throw the exceptions defined in its contract. But that's not the question. The question is how you decide what that contract should be. I maintain that the rule of thumb, "exceptions should be exceptional" isn't helpful in making that decision. Commented Nov 21, 2013 at 5:58
  • 1
    @supercat, I don't think it really matters which ends up being more common. I think the critical question is having a sane default. If I don't explicitly handle the error condition, does my code pretend nothing happened, or do I get a useful error message? Commented Apr 21, 2015 at 4:34
29

I always think of things like accessing the database server or a web API when thinking of exceptions. You expect the server/web API to work, but in an exceptional case it might not (server is down). A web request might be quick usually, but in exceptional circumstances (high load) it might time out. This is something out of your control.

You users' input data is in your control, since you can check what they send in and do with it what you like. In your case, I'd validate the user input before even trying to save it. And I tend to agree that users providing invalid data should be expected, and your app should account for it by validating the input and providing the user-friendly error message.

That said, I do use exceptions in most of my domain model setters, where there should be absolutely no chance that invalid data goes in. However, this is a last line of defense, and I tend to build my input forms with rich validation rules, so that there's practically no chance of triggering that domain model exception. So when a setter is expecting one thing, and gets another, it is an exceptional situation, which should not have happened in ordinary circumstances.

EDIT (something else to consider):

When sending user provided data to the db, you know beforehand what you should and shouldn't enter into your tables. This means that data can be validated against some expected format. This is something you can control. What you can't control is your server failing in the middle of your query. So you know the query is ok and data is filtered/validated, you try the query and it still fails, this is an exceptional situation.

Similarly with the web requests, you can't know if the request will time out, or fail to connect before you try sending it. So this also warrants a try/catch approach, since you can't ask the server if it will work a few milliseconds later when you send the request.

13
  • 9
    But why? Why are exceptions less useful in handling problems that are more expected? Commented Jan 24, 2013 at 14:59
  • 8
    @Pinetree, checking for file existence before opening a file is bad idea. The file could cease to exist between the check and the open, the file could not have permission that let you open it, and checking for existence and then opening the file will require two expensive system calls. You are better off trying to open the file and then dealing with the failure to do so. Commented Jan 24, 2013 at 15:20
  • 12
    As far as I can see, pretty much all possible failures are better handled as recovering from the failure rather then trying to check for success before hand. Whether or not you use exceptions or something else indicate failure is a separate issue. I prefer exceptions because I can't accidentally ignore them. Commented Jan 24, 2013 at 15:23
  • 12
    I disagree with your premise that because invalid user data is expected, it can not be considered exceptional. If I write a parser, and someone feeds it unparsable data, that is an exception. I can not continue parsing. How the exception is handled is another question entirely. Commented Jan 24, 2013 at 17:40
  • 4
    File.ReadAllBytes will throw a FileNotFoundException when given the wrong input (for example a non-existant filename). That is the only valid way to threat this error, what else can you do without resulting to returning error codes?
    – oɔɯǝɹ
    Commented Jan 24, 2013 at 21:49
19

Reference

From The Pragmatic Programmer:

We believe that exceptions should rarely be used as part of a program's normal flow; exceptions should be reserved for unexpected events. Assume that an uncaught exception will terminate your program and ask yourself, "Will this code still run if I remove all the exception handlers?" If the answer is "no," then maybe exceptions are being used in nonexceptional circumstances.

They go on to examine the example of opening a file for reading, and the file doesn't exist - should that raise an exception?

If the file should have been there, then an exception is warranted. [...] On the other hand, if you have no idea whether the file should exist or not, then it doesn't seem exceptional if you can't find it, and an error return is appropriate.

Later, they discuss why they chose this approach:

[A]n exception represents an immediate, nonlocal transfer of control - it's a kind of cascading goto. Programs that use exceptions as part of their normal processing suffer from all the readability and maintainability problems of classic spaghetti code. These programs break encapsulation: routines and their callers are more tightly coupled via exception handling.

Regarding your situation

Your question boils down to "Should validation errors raise exceptions?" The answer is that it depends on where the validation is happening.

If the method in question is within a section of the code where it is assumed that input data has already been validated, invalid input data should raise an exception; if the code is designed such that this method will receive the exact input entered by a user, invalid data is to be expected, and an exception should not be raised.

11

There's a lot of philosophical pontification here, but generally speaking, exceptional conditions are simply those conditions which you cannot or don't want to handle (other than cleanup, error reporting, and the like) without user intervention. In other words, they are unrecoverable conditions.

If you hand a program a file path, with the intention of processing that file in some way, and the file specified by that path doesn't exist, that's an exceptional condition. You cannot do anything about that in your code, other than report it to the user and allow them to specify a different file path.

3
  • 1
    +1, very close to what I was going to say. I would say it is more about scope, and really has nothing to do with the user. A good example of this is the difference between the two .Net functions int.Parse and int.TryParse, the former has no choice but to throw an exception on bad input, the later should never throw an exception
    – jmoreno
    Commented Jan 24, 2013 at 16:25
  • 1
    @jmoreno: Ergo, you would use TryParse when the code could do something about the unparseable condition, and Parse when it could not. Commented Jan 24, 2013 at 16:26
  • Some people would prefer to call such a condition a "panic", instead of an exception, which is catchable within the program. Commented Oct 1, 2023 at 18:13
8

There are two concerns you should consider:

  1. you discuss a single concern - let's call it Assigner since this concern is to assign inputs to structured objects - and you express the constraint that its inputs be valid

  2. a well-implemented user-interface has an additional concern: validation of user input & constructive feedback on errors (let's call this part Validator)

From the point of view of the Assigner component, throwing an exception is totally reasonable, since you've expressed a constraint that has been violated.

From the point of view of the user experience, the user shouldn't be talking directly to this Assigner in the first place. They should be talking to it via the Validator.

Now, in the Validator, invalid user input is not an exceptional case, it's really the case you're more interested in. So here an exception wouldn't be appropriate, and this is also where you'd want to identify all errors rather than bailing out on the first.

You'll notice I didn't mention how these concerns are implemented. It seems you're talking about the Assigner and your colleague is talking about a combined Validator+Assigner. Once you realise there are two separate (or separable) concerns, at least you can discuss it sensibly.


To address Renan's comment, I'm just assuming that once you've identified your two separate concerns, it's obvious what cases should be considered exceptional in each context.

In fact, if it isn't obvious whether something should be considered exceptional, I'd argue you probably haven't finished identifying the independent concerns in your solution.

I guess that makes the direct answer to

... how do I know if my case is exceptional?

keep simplifying until it is obvious. When you have a pile of simple concepts you understand well, you can reason clearly about composing them back into code, classes, libraries or whatever.

3
  • -1 Yep, there are two concerns, but this does not answer the question "How do I know if my case is exceptional?"
    – RMalke
    Commented Jan 24, 2013 at 11:45
  • The point is the same case can be exceptional in one context, and not in another. Identifying which context you're actually talking about (rather than conflating them both) answers the question here.
    – Useless
    Commented Jan 24, 2013 at 11:47
  • ... actually, maybe it doesn't - I've addressed your point in my answer, instead.
    – Useless
    Commented Jan 24, 2013 at 11:56
4

Others have answered well, but still here is my short answer. Exception is situation where something in the environment has wrong, which you can not control and your code can not go forward at all. In this case you will also have to inform the user what went wrong, why you cannot go further, and what is the resolution.

3

I've never been a great fan of the advice that you should only throw exceptions in cases that are exceptional, partly because it doesn't say anything (it's like saying that you should only eat food that is edible), but also because it is very subjective, and it's often not clear what constitutes an exceptional case and what doesn't.

However, there are good reasons for this advice: throwing and catching exceptions is slow, and if you are running your code in the debugger in Visual Studio with it set to notify you whenever an exception is thrown, you can end up being spammed by dozens if not hundreds of messages long before you get to the problem.

So as a general rule, if:

  • your code is bug free, and
  • the services on which it depends are all available, and
  • your user is using your program in the way that it was intended to be used (even if some of the input they provide is invalid)

then your code should never throw an exception, even one that is caught later. To trap invalid data, you can use validators at the UI level or code such as Int32.TryParse() in the presentation layer.

For anything else, you should stick to the principle that an exception means that your method can not do what its name says that it does. In general it's not a good idea to use return codes to indicate failure (unless your method name clearly indicates that it does so, e.g. TryParse()) for two reasons. First, the default response to an error code is to ignore the error condition and carry on regardless; second, you can all too easily end up with some methods using return codes and other methods using exceptions, and forgetting which is which. I have even seen codebases where two different interchangeable implementations of the same interface take different approaches here.

2

Exceptions should represent conditions that it's likely the immediate calling code will not be prepared to handle, even if the calling method might. Consider, for example, code which is reading some data from a file, may legitimately assume that any valid file will end with a valid record, and is not required to extract any information from a partial record.

If the read-data routine didn't use exceptions but simply reported whether or not the read succeeded, the calling code would have to look like:

temp = dataSource.readInteger();
if (temp == null) return null;
field1 = (int)temp;
temp = dataSource.readInteger();
if (temp == null) return null;
field2 = (int)temp;
temp = dataSource.readString();
if (temp == null) return null;
field3 = temp;

etc. spending three lines of code for each useful piece of work. By contrast, if readInteger will throws an exception upon encountering the end of a file, and if the caller can simply pass on the exception, then the code becomes:

field1 = dataSource.readInteger();
field2 = dataSource.readInteger();
field3 = dataSource.readString();

Much simpler and cleaner looking, with far greater emphasis on the case where things work normally. Note that in cases where the immediate caller would be expecting to handle a condition, a method which returns an error code will often be more helpful than one which throws an exception. For example, to total all the integers in a file:

do
{
  temp = dataSource.tryReadInteger();
  if (temp == null) break;
  total += (int)temp;
} while(true);

versus

try
{
  do
  {
    total += (int)dataSource.readInteger();
  }
  while(true);
}
catch endOfDataSourceException ex
{ // Don't do anything, since this is an expected condition (eventually)
}

The code which asking for the integers is expecting that one of those calls is going to fail. Having the code use an endless loop which will run until that happens is far less elegant than using a method that indicates failures via its return value.

Because classes often won't know what conditions their clients will or will not expect, it's often helpful to offer two versions of methods that could fail in ways that some callers will expect and other callers won't. Doing so will allow such methods to be used cleanly with both types of callers. Note also that even "try" methods should throw exceptions if situations arise the caller probably isn't expecting. For example, tryReadInteger should not throw an exception if it encounters a clean end-of-file condition (if the caller weren't expecting that, the caller would have used readInteger). On the other hand, it probably should throw an exception if the data could not be read because e.g. the memory stick containing it was unplugged. While such events should always be recognized as a possibility, it's unlikely that the immediate calling code would be prepared to do anything useful in response; it should certainly not be reported the same way as would be an end-of-file condition.

2

The most important thing in writing software is making it readable. All other considerations are secondary, including making it efficient and making it correct. If it's readable, the rest can be taken care of in maintenance, and if it's not readable then you're better off just throwing it away. Therefore, you should throw exceptions when it enhances readability.

When you're writing some algorithm, just think about the person in the future who will be reading it. When you come to a place where there could be a potential problem, ask yourself if the reader wants to see how you handle that problem now, or would the reader prefer to just get on with the algorithm?

I like to think of a recipe for chocolate cake. When it tells you to add the eggs, it has a choice: it can either assume you have eggs and get on with the recipe, or it can begin an explanation for how you can get eggs if you don't have eggs. It could fill a whole book with techniques for hunting wild chickens, all to help you bake a cake. That's good, but most people aren't going to want to read that recipe. Most people would prefer to just assume that eggs are available, and get on with the recipe. That's a judgement call that authors need to make when writing recipes.

There can't be any guaranteed rules about what makes a good exception and what problems should be handled immediately, because it requires you to read the mind of your reader. The best you'll ever do is rules of thumb, and "exceptions are only for exceptional circumstances" is a pretty good one. Usually when a reader is reading your method they are looking for what the method will do 99% of the time, and they'd rather not have that cluttered with bizarre corner cases like dealing users entering illegal input and other stuff that almost never happens. They want to see the normal flow of your software laid out directly, one instruction after another as if problems never happen. Understanding your program is going to be hard enough without having to deal with constantly going off on tangents to deal with every little problem that might come up.

1
  • the most important thing about code is: does it work?
    – Kirby
    Commented Sep 17, 2022 at 4:50
2

There may be a requirement to report on every error we can find in the input, not just the first.

This is why you cannot throw an exception here. An exception immediately interrupts the validation process. So there would be much work-around to get this done.

A bad example:

Validation method for Dog class using exceptions:

void validate(Set<DogValidationException> previousExceptions) {
    if (!DOG_NAME_PATTERN.matcher(this.name).matches()) {
        DogValidationException disallowedName = new DogValidationException(Problem.DISALLOWED_DOG_NAME);
        if (!previousExceptions.contains(disallowedName)){
            throw disallowedName;
        }
    }
    if (this.legs < 4) {
        DogValidationException invalidDog = new DogValidationException(Problem.LITERALLY_INVALID_DOG);
        if (!previousExceptions.contains(invalidDog)){
            throw invalidDog;
        }
    }
    // etc.
}

How to call it:

Set<DogValidationException> exceptions = new HashSet<DogValidationException>();
boolean retry;
do {
    retry = false;
    try {
        dog.validate(exceptions);
    } catch (DogValidationException e) {
        exceptions.add(e);
        retry = true;
    }
} while (retry);

if(exceptions.isEmpty()) {
    dogDAO.beginTransaction();
    dogDAO.save(dog);
    dogDAO.commitAndCloseTransaction();
} else {
    // notify user to fix the problems
}

The problem here is that the validation process, to get all errors, would require to skip already found exceptions. The above could work, but this is a clear misuse of exceptions. The kind of validation you were asked for should take place before the database is touched. So there is no need to roll back anything. And, the results of the validation are likely to be validation errors (hopefully zero, though).

The better approach is:

Method call:

Set<Problem> validationResults = dog.validate();
if(validationResults.isEmpty()) {
    dogDAO.beginTransaction();
    dogDAO.save(dog);
    dogDAO.commitAndCloseTransaction();
} else {
    // notify user to fix the problems
}

Validation method:

Set<Problem> validate() {
    Set<Problem> result = new HashSet<Problem>();
    if(!DOG_NAME_PATTERN.matcher(this.name).matches()) {
        result.add(Problem.DISALLOWED_DOG_NAME);
    }
    if(this.legs < 4) {
        result.add(Problem.LITERALLY_INVALID_DOG);
    }
    // etc.
    return result;
}

Why? There are tons of reasons, and the most reasons have been pointed out in the other responses. To put it simple: It is much simpler to read and understand by others. Second, do you want to show the user stack traces to explain him he set up his dog wrongly?

If, during the commit in the second example, still an error arises, even though your validator validated the dog with zero issues, then throwing an exception is the right thing. Like: No database connection, the database entry has been modified by someone else meanwhile, or that like.

1
  • Pretty good answer. I would like to add that sometimes things are not this simple. Consider a case where modifying dog is only one part of the larger logical change that must be applied to database as one atomic change. In that case one may succeed here with validate() and still need to rollback due some other object later failing the validation. A good framework would support doing validation round over all objects taking part in transaction and then doing another round over all objects to commit changes. Commented May 3, 2020 at 8:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.