In order to handle several possible errors that shouldn't halt execution, I have an error
variable that clients can check and use to throw exceptions. Is this an Anti-Pattern? Is there a better way to handle this? For an example of this in action you can see PHP's mysqli API. Assume that visibility problems (accessors, public and private scope, is the variable in a class or global?) are handled correctly.
11 Answers
If a language inherently supports exceptions, then it is preferred to throw exceptions and the clients can catch the exception if they do not want it to result in a failure. In fact, the clients of your code expect exceptions and will run into many bugs because they will not be checking the return values.
There are quite a few advantages to using exceptions if you have a choice.
Messages
Exceptions contain user readable error messages which can be used by the developers for debugging or even displayed to the users if so desired. If the consuming code cannot handle the exception, it can always log it so the developers can go through the logs without having to stop at every other trace to figure out what was the return value and map it in a table to figure out what was the actual exception.
With return values, there is no additional information can be easily provided. Some languages will support making method calls to get the last error message, so this concern is allayed a bit, but that requires the caller to make extra calls and sometimes will require access to a 'special object' that carries this information.
In the case of exception messages, I provide as much context as possible, such as:
A policy of name "foo" could not be retrieved for user "bar", which was referenced in user's profile.
Compare this to a return code -85. Which one would you prefer?
Call stacks
Exceptions usually also have detailed call stacks which help debug code faster and quicker, and can also be logged by the calling code if so desired. This allows the developers to pinpoint the issue usually to the exact line, and thus is very powerful. Once again, compare this to a log file with return values (such as a -85, 101, 0, etc.), which one would you prefer?
Fail fast biased approach
If a method is called somewhere that fails, it will throw an exception. The calling code has to either suppress the exception explicitly or it will fail. I have found this to be actually amazing because during development and testing (and even in production) the code fails quickly, forcing the developers to fix it. In the case of return values, if a check for a return value is missed, the error is silently ignored and the bug surfaces somewhere unexpected, usually with a much higher cost to debug and fix.
Wrapping and Unwrapping Exceptions
Exceptions can be wrapped inside other exceptions and then unwrapped if needed. For example, your code might throw ArgumentNullException
which the calling code might wrap inside a UnableToRetrievePolicyException
because that operation had failed in the calling code. While the user might be shown a message similar to the example I provided above, some diagnostic code might unwrap the exception and find that an ArgumentNullException
had caused the issue, which means it is a coding error in your consumer's code. This could then fire an alert so the developer can fix the code. Such advanced scenarios are not easy to implement with the return values.
Simplicity of code
This one is a bit harder to explain, but I learnt through this coding both with return values as well as exceptions. The code that was written using return values would usually make a call and then have a series of checks on what the return value was. In some cases, it would make call to another method, and now will have another series of checks for the return values from that method. With exceptions, the exception handling is far simpler in most if not all cases. You have a try/catch/finally blocks, with the runtime trying its best to execute the code in the finally blocks for clean-up. Even nested try/catch/finally blocks are relatively easier to follow through and maintain than nested if/else and associated return values from multiple methods.
Conclusion
If the platform you are using supports exceptions (esp. such as Java or .NET), then you should definitely assume that there is no other way except to throw exceptions because these platforms have guidelines to throw exceptions, and your clients are going to expect so. If I were using your library, I will not bother to check the return values because I expect exceptions to be thrown, that's how the world in these platforms is.
However, if it were C++, then it would be a bit more challenging to determine because a large codebase already exists with return codes, and a large number of developers are tuned to return values as opposed to exceptions (e.g. Windows is rife with HRESULTs). Furthermore, in many applications, it can be a performance issue too (or at least perceived to be).
-
5Windows returns HRESULT values from C++ functions to maintain C compatibility in its public APIs (and because you start getting into a world of hurt trying to marshal exceptions across boundaries). Don't blindly follow the operating system's model if you're writing an application. Jun 17, 2014 at 8:27
-
2The only thing I would add to this is mention of looser coupling. Exceptions allow you to handle a lot of unexpected situations in the most appropriate place. For example, in a web app, you want to return a 500 on any exception your code wasn't prepared for, rather than crash the web app. So you need some kind of catch all at the top of your code (or in your framework). A similar situation exists in desktop GUIs. But you can also put less general handlers in various places in the code to handle a variety of failure situations in a way appropriate for the current process being attempted.– jpmc26Jun 17, 2014 at 9:53
-
2@TrentonMaki If you're talking about errors from C++ constructors, the best answer is here: parashift.com/c++-faq-lite/ctors-can-throw.html. In short, throw an exception, but remember to clean up potential leaks first. I'm not aware of any other languages where just straight throwing from a constructor is a bad thing. Most users of an API I think would prefer to catch exceptions rather than check error codes. Jun 17, 2014 at 14:31
-
3"Such advanced scenarios are not easy to implement with the return values." Sure you can! All you have to do is create an
ErrorStateReturnVariable
super-class, and one of its properties isInnerErrorState
(which is an instance ofErrorStateReturnVariable
), which implementing sub-classes can set to show a chain of errors... oh, wait. :p– Brian SJun 17, 2014 at 22:25 -
5Just because a language supports exceptions does not make them a panacea. Exceptions introduce hidden execution-paths, and thus their effects need be properly controlled; try/catch are easy to add, but getting the recovery right is hard, ... Jun 18, 2014 at 8:18
There are multiple ways to signal an error:
- an error variable to check: C, Go, ...
- an exception: Java, C#, ...
- a "condition" handler: Lisp (only ?), ...
- a polymorphic return: Haskell, ML, Rust, ...
The problem of the error variable is that it is easy to forget to check.
The problem of exceptions is that is creates hidden paths of executions, and, although try/catch is easy to write, ensuring a proper recovery in the catch clause is really difficult to pull off (no support from type systems/compilers).
The problem of condition handlers is that they do not compose well: if you have dynamic code execution (virtual functions), then it is impossible to predict which conditions should be handled. Furthermore, if the same condition can be raised in several spots, there is no telling that a uniform solution can be applied each time, and it quickly becomes messy.
Polymorphic returns (Either a b
in Haskell) are my favorite solution so far:
- explicit: no hidden path of execution
- explicit: fully documented in function type signature (no surprises)
- hard to ignore: you have to pattern match to get the desired result out, and handle the error case
The only issue is that they may potentially lead to excessive checking; the languages that use them have idioms to chain the calls of functions that use them, but it may still require a bit more typing/clutter. In Haskell this would be monads; however, this is far scarier than it sounds, see Railway Oriented Programming.
-
1Good answer! I was hoping I might get a list of ways to handler errors. Jun 18, 2014 at 20:27
-
Arrghhhh! How many times have I seen this "The problem of the error variable is that it is easy to forget to check". The problem with exceptions is that it is easy to forget to catch it. Then your application crashes. Your boss doesn't want to pay to fix it but your customers stop using your app because they get frustrated with the crashes. All because of something that would not have affected program execution if error codes were returned and ignored. The only time I've ever seen people ignore error codes is when it didn't matter that much. Code higher up the chain knows something went wrong.– DunkJul 8, 2014 at 14:45
-
1@Dunk: I don't think that my answer makes the apology of exceptions either; though, it might well depend on the type of code you write. My personal work experience tends to favor systems that fail in the presence of errors because silent data corruption is worse (and undetected) and the data I work on is valuable to client (of course, it also mean urgent fixes). Jul 8, 2014 at 14:48
-
It isn't a question of silent data corruption. It is a question of understanding your app and knowing when and where you need to verify if an operation was successful or not. In many cases, making that determination and handling it can be delayed. It shouldn't be up to somebody else to tell me when I have to handle a failed operation, which an exception requires. It's my app, I know when and where I want to handle any relevant issues. If people write apps that could corrupt data then that's just doing a really poor job. Writing apps that crash (which I see a lot) is also doing a poor job.– DunkJul 9, 2014 at 17:53
Error variables are a relic from languages like C, where exceptions were not available. Today, you should avoid them except when you are writing a library which is potentially used from a C program (or a similar language without exception handling).
Of course, if you have a type of error which could be better classified as "warning" (= your library can deliver a valid result and the caller can ignore the warning if he thinks it is not important), then a status indicator in form of a variable can make sense even in languages with exceptions. But beware. Callers of the library tend to ignore such warnings even if they should not. So think twice before introducing such a construct into your lib.
-
1Thanks for the explanation! Your answer + Omer Iqbal answered my question. Jun 17, 2014 at 14:06
-
Another way to handle "warnings" is to throw an exception by default and have some kind of optional flag to stop the exception from being thrown.– CyanfishJun 17, 2014 at 19:29
-
1@Cyanfish: yes, but one has to be careful not to overdesign such things, especially when making libraries. Better provide one simple and working warning mechanism than 2, 3 or more. Jun 17, 2014 at 19:37
-
An exception should only be thrown when a failure, unknown or irrecoverable scenario has been experienced - exceptional circumstances. You should expect performance impact when constructing exceptions– GusdorJun 18, 2014 at 9:55
-
@Gusdor: absolutely, that's why a typical "warning" should IMHO not throw an exception by default. But this also depends a little bit on the level of abstraction. Sometimes a service or library simply cannot decide if an unusual event should be treated as an exception from the callers point of view. Personally, in such situations I prefer the lib just to set a warning indicator (no exception), let the caller test that flag and throw an exception if he thinks its appropriate. That's is what I had in mind when I wrote above "its better to provide one warning mechanism in a libary". Jun 18, 2014 at 17:16
I think it's awful. I'm currently refactoring a Java app that uses return values instead of exceptions. Although you may not at all be working with Java, I think this applies nonetheless.
You end up with code like this:
String result = x.doActionA();
if (result != null) {
throw new Exception(result);
}
result = x.doActionB();
if (result != null) {
throw new Exception(result);
}
Or this:
if (!x.doActionA()) {
throw new Exception(x.getError());
}
if (!x.doActionB()) {
throw new Exception(x.getError());
}
I'd much rather have the actions throw exceptions themselves, so you end up with something like:
x.doActionA();
x.doActionB();
You can wrap that in a try-catch, and get the message from the exception, or you can choose to ignore the exception, for example when you're deleting something that may already be gone. It also preserves your stack trace, if you have one. The methods themselves become easier, too. Instead of handling the exceptions themselves, they just throw what went wrong.
Current (horrible) code:
private String doActionA() {
try {
someOperationThatCanGoWrong1();
someOperationThatCanGoWrong2();
someOperationThatCanGoWrong3();
return null;
} catch(Exception e) {
return "Something went wrong!";
}
}
New and improved:
private void doActionA() throws Exception {
someOperationThatCanGoWrong1();
someOperationThatCanGoWrong2();
someOperationThatCanGoWrong3();
}
Strack trace is preserved and the message is available in the exception, rather than the useless "Something went wrong!".
You can, of course, supply better error messages, and you should. But this post is here because the current code I'm working on is a pain, and you shouldn't do the same.
-
1One catch though, there are situations in which your 'new and improved' would lose the context of where the exception initially happens. For example in doActionA()'s "current (horrible) version", the catch clause would have access to instance variables and other information from the enclosing object to give a more useful message. Jun 17, 2014 at 7:05
-
2That is true, but doesn't currently happen, here. And you can always catch the exception in doActionA and wrap it in another exception with a status message. Then you will still have the stack trace and the useful message.
throw new Exception("Something went wrong with " + instanceVar, ex);
– mrjinkJun 17, 2014 at 7:09 -
I agree, it might not happen in your situation. But you cannot "always" put the information in doActionA(). Why? The caller of doActionA() might be the only one holding the information that you need to include. Jun 17, 2014 at 7:12
-
3So, have the caller include it when it handles the exception. The same applies to the original question, though. There is nothing you can do now that you cannot do with exceptions, and it leads to cleaner code. I prefer exceptions over returned booleans or error messages.– mrjinkJun 17, 2014 at 7:17
-
You make the usually false assumption that an exception happening within any of the "someOperation"s can be handled and cleaned up in the same way. What happens in real life is that you need to catch and handle exceptions for each operation. Thus, you don't just throw an exception as in your example. Also, using exceptions then ends up creating either a bunch of nested try-catch blocks or a series of try-catch blocks. It frequently makes the code far less readable. I'm not set against exceptions, but I use the appropriate tool for the specific situation. Exceptions are just 1 tool.– DunkJul 9, 2014 at 18:02
"In order to handle several possible errors happening, that shouldn't halt execution,"
If you mean that the errors should not halt execution of the current function, but should be reported to the caller in some way - then you have a few options that have not really been mentioned. This case is really more a warning than an error. Throwing/Returning is not an option as that ends the current function. A single error message paramter or return only allows for at most one of these errors to occur.
Two patterns that I've used are:
An error/warning collection, either passed in or kept as a member variable. Which you append stuff to and just keep processing. I personally don't really like this approach as I feel it disempowers the caller.
Pass in an error/warning handler object (or set it as a member variable). And each error calls a member function of the handler. This way the caller can decide what to do with such non-terminating errors.
What you pass to these collections/handlers should contain enough context for the error to be handled "correctly" - A string is usually too little, passing it some instance of Exception is often sensible - but sometimes frowned upon (as an abuse of Exceptions).
Typicial code using an error handler might look like this
class MyFunClass {
public interface ErrorHandler {
void onError(Exception e);
void onWarning(Exception e);
}
ErrorHandler eh;
public void canFail(int i) {
if(i==0) {
if(eh!=null) eh.onWarning(new Exception("canFail shouldn't be called with i=0"));
}
if(i==1) {
if(eh!=null) eh.onError(new Exception("canFail called with i=1 is fatal");
throw new RuntimeException("canFail called with i=2");
}
if(i==2) {
if(eh!=null) eh.onError(new Exception("canFail called with i=2 is an error, but not fatal"));
}
}
}
-
3+1 for noticing that the user wants a warning, rather than an error. Might be worth mentioning Python's
warnings
package, which gives another pattern for this problem. Jun 18, 2014 at 9:07 -
Thanks for the great answer! This is more of what I wanted to see, additional patterns for handling errors where the traditional try/catch might not suffice. Jun 18, 2014 at 20:24
-
It might be useful to mention that a passed-in error-callback object can throw an exception when certain errors or warnings occur--indeed, that might be the default behavior--but it may also be useful to means by which it can ask the calling function to do something. For example, an "handle parsing error" method might give the caller a value that the parse should be deemed to have returned.– supercatAug 19, 2014 at 21:43
There is often nothing wrong with using this pattern or that pattern, as long as you use the pattern that everyone else uses. In Objective-C development, the much preferred pattern is to pass a pointer where the method that is called can deposit an NSError object. Exceptions are reserved for programming errors and lead to a crash (unless you have Java or .NET programmers writing their first iPhone app). And this works quite well.
The question is already answered, but I can't help myself.
You can't really expect Exception to provide a solution for all use cases. Hammer anyone?
There are cases where Exceptions are not the end all and be all, for example, if a method receives a request and is responsible for validating all the fields passed, and not only the first one then you have to think that it should be possible to indicate the cause of the error for more than one fields. It should be possible to also indicate if the nature of the validation prevents the user from going further or not. An example of that would be a not strong password. You could show a message to the user indicating that the entered password is not very strong, but that it is strong enough.
You could argue that all of these validations could be thrown as an exception at the end of the validation module, but they would be error codes in anything but in the name.
So the lesson here is: Exceptions have their places, as do error codes. Chose wisely.
-
I think this rather implies a bad design. A method that takes arguments should be able to deal with those or not - nothing in-between. Your example should have a
Validator
(interface) injected into the method in question (or the object behind it). Depending on the injectedValidator
the method will proceed with bad passwords - or not. The surrounding code could then try aWeakValidator
if the user asked for it after, e.g., aWeakPasswordException
was thrown by the initially triedStrongValidator
.– jhrJun 18, 2014 at 14:02 -
Ah, but I didn't say this couldn't be an interface, or a validator. I didn't even mention JSR303. And, if you read carefully, I made sure not to say weak password, rather, that it was not very strong. A weak password would be a reason to stop the flow and ask the user for a stronger password. Jun 18, 2014 at 15:49
-
And what would you do with a middly-strong-but-not-really-weak password? You'd interrupt the flow and show the user a message indicating that the entered password is not very strong. So have a
MiddlyStrongValidator
or something. And if that didn't really interrupt your flow, theValidator
must have been called beforehand, that is before proceeding the flow while the user is still entering their password (or similar). But then the validation wasn't part of the method in question in the first place. :) Probably a matter of taste after all...– jhrJun 18, 2014 at 20:30 -
@jhr In the validators I have written, I will typically create an
AggregateException
(or a similarValidationException
), and put specific exceptions for each validation issue in InnerExceptions. For example, it could beBadPasswordException
: "The user's password is less than the minimum length of 6" orMandatoryFieldMissingException
: "The first name must be provided for the user" etc. This is not equivalent to error codes. All of these messages can be displayed to the user in a way that they will understand, and if aNullReferenceException
was thrown instead, then we got a bug. Jun 25, 2014 at 7:46
There are use cases were error codes are preferable to exceptions.
If your code can continue despite the error, but it needs reporting, then an exception is a poor choice because exceptions terminate the flow. For example, if you're reading in a data file and discover it contains some non-terminal piece of bad data it might be better to read in the rest of the file and report the error rather than fail outright.
The other answers have covered why exceptions should be preferred to error codes in general.
-
If a warning needs to be logged or something, so be it. But if an error "needs reporting", by which I assume you mean reporting to the user, there is no way to guarantee that surrounding code will read your return code and actually report it.– jhrJun 18, 2014 at 14:05
-
No, I mean reporting it to the caller. It's the caller's responsibility to decide whether the user needs to know about the error or not, just as with an exception. Jun 18, 2014 at 14:41
-
1@jhr: When is there ever any guarantee of anything? The contract for a class may specify that clients have certain responsibilities; if clients abide by the contract, they will do the things the contract requires. If they don't, any consequences will be the fault of the client code. If one wants to guard against accidental mistakes by the client and has control over the type returned by a serializing method, one could have it include an "unacknowledged possible corruption" flag and not allow clients to read data from it without calling an
AcknowledgePossibleCorruption
method...– supercatJun 18, 2014 at 17:58 -
1...but having an object class to hold information about problems may be more helpful than throwing exceptions or returning a pass-fail error code. It would be up to the application to use that information in suitable fashion (e.g. when loading file "Foo", inform the user that data may not be reliable, and prompt the user to choose a new name when saving).– supercatJun 18, 2014 at 18:01
-
1There is one guarantee if using exceptions: If you don't catch them, they throw higher up - worst case up to the UI. No such guarantee if you use return codes that no-one reads. Sure, follow the API if you wanna use it. I agree! But there's room for error, unfortunately...– jhrJun 18, 2014 at 20:32
There's definitely nothing wrong with not using exceptions when exceptions are not a good fit.
When the code execution should not be interrupted (e.g. acting on user input that may contain multiple errors, like a program to compile or a form to process), I find that collecting errors in error variables like has_errors
and error_messages
is indeed far more elegant design than throwing an exception at the first error. It allows to find all errors in user input without forcing the user to resubmit unnecessarily.
-
Interesting take on the question. I think the problem with my question, and my understanding, is unclear terminology. What you describe is not exceptional, but it is an error. What should we call that? Jul 7, 2014 at 21:40
In some dynamic programming languages you can use both error values and exception handling. This is done by returning unthrown exception object in place of ordinary return value, which can be checked like an error value, but it throws an exception if it isn't checked.
In Perl 6 it is done via fail
, which if withing no fatal;
scope returns a special unthrown exception Failure
object.
In Perl 5 you can use Contextual::Return you can do this with return FAIL
.
Unless there is something very specific, I think having an error variable for validation is a bad idea. The purpose seems to be about saving the time spent on validation (you can just return the variable value)
But then if you change anything, you have to recompute that value anyway. I can't say more about halting and Exception throwing though.
EDIT: I didn't realize this is a question of software paradigm instead of a specific case.
Let me further clarify my points in a particular case of mine in which my answer would make sense
- I have a collections of entity objects
- I have procedural style web services which work with these entity objects
There are two kinds of errors:
- The errors when processing happens in the service layer
- The errors because there are inconsistencies in the entity objects
In the service layer, there is no choice but using Result object as wrapper which is the equivalence of error variable. Simulating an exception via service call on protocol like http is possible, but is definitely not a good thing to do. I am not talking about this kind of error and didn't think this is the kind of error being asked in this question.
I was thinking about the second kind of error. And my answer is about this second kind of errors. In the entity objects, there are choices for us, some of them are
- using a validation variable
- throw an exception immediately when a field is set incorrectly from the setter
Using validation variable is the same as having a single validation method for each entity object. In particular, the user can set the value in a way that keep the setter as purely setter, no side effect (this is often a good practice) or one can incorporate validation into each setter and then save the result into a validation variable. The advantage of this would be to save time, the validation result is cached into the validation variable so that when the user calls validation() multiple time, there is no need to do multiple validation.
The best thing to do in this case is using a single validation method without even using any validation to cache validation error. This helps keep the setter as just setter.
-
I see what you're talking about. Interesting approach. I'm glad it's part of the answer set. Jul 8, 2014 at 16:42
try
/catch
exists for. Additionally, you can put yourtry
/catch
much further up the stack in a more appropriate location for handling it (allowing for greater separation of concerns).