9

I have been reading the advice on this question about how an exception should be dealt with as close to where it is raised as possible.

My dilemma on the best practice is whether one should use a try/catch/finally to return an enum (or an int that represents a value, 0 for error, 1 for ok, 2 for warning etc, depending on the case) so that an answer is always in order, or should one let the exception go through so that the calling part would deal with it?

From what I can gather, this might be different depending on the case, so the original advice seems odd.

For example, on a web service, you would always want to return a state of course, so any exception has to be dealt with on the spot, but lets say inside a function that posts/gets some data via http, you would want the exception (for example in case of 404) to just pass through to the one that fired it. If you don't, you would have to create some way to inform the calling part of the quality of the outcome (error:404), as well as the outcome itself.

Though it IS possible to try-catch the 404 exception inside the helper function that gets/posts the data, should you? Is it only I that use a smallint to denote states in the program (and document them appropriately of course), and then use this info for sanity validation purposes (everything ok/error handling) outside?

Update: I was expecting a fatal/non-fatal exception for the main classification, but I didn't want to include this so as not to prejudice the answers. Let me clarify what the question is about: Handling the exceptions thrown, not throwing exceptions. What the desired effect is: Detect an error, and try to recover from it. If recovery isn't possible, provide the most meaningful feedback.

Again, with the http get/post example, the question is, should you provide a new object that describes what happened to the original caller? If this helper was in a library you are using would you expect it to provide you with a status code for the operation, or would you include it in a try-catch block? If you are designing it, would you provide a status code or throw an exception and let the upper level translate it to a status code/message instead?

Synopsis: How do you chose if a piece of code instead of producing an exception, returns a status code along with any results it may yield?

2
  • 1
    That isn't dealing with the error that is changing the form of error handling being used. In the 404 case you would let it pass through because you are unable to handle it.
    – stonemetal
    Feb 15, 2012 at 18:56
  • "an int that represents a value, 0 for error, 1 for ok, 2 for warning etc" Please say this was an off-the-cuff example! Using 0 to mean OK is very definitely the standard... May 15, 2013 at 12:59

8 Answers 8

15

Exceptions should be used for exceptional conditions. Throwing an exception is basically making the statement, "I can't handle this condition here; can someone higher up on the call stack catch this for me and handle it?"

Returning a value can be preferable, if it is clear that the caller will take that value and do something meaningful with it. This is especially true if throwing an exception has performance implications, i.e. it may occur in a tight loop. Throwing an exception takes much longer than returning a value (by at least two orders of magnitude).

Exceptions should never be used to implement program logic. In other words, don't throw an exception to get something done; throw an exception to state that it couldn't be done.

3
  • Thanks for the reply, it's the most informative but my focus is on exception handling, and not exception throwing. Should you catch the 404 exception as soon as you receive it or should you let it go higher up the stack? Feb 17, 2012 at 16:12
  • I see your edit, but it doesn't change my answer. Convert the exception to an error code if that is meaningful to the caller. If it is not, handle the exception; let it go up the stack; or catch it, do something with it (like write it to a log, or something else), and rethrow. Feb 17, 2012 at 16:43
  • 1
    +1: for a reasonable and balanced explanation. An exception should be used to handle exceptional cases. Otherwise, a function or method should return a meaningful value (enum or option type) and the caller should handle it properly. Maybe one could mention a third alternative that is popular in functional programming, i.e. continuations.
    – Giorgio
    May 15, 2013 at 10:57
4

Some good advice I once read was, throw exceptions when you cannot progress given the state of the data you are dealing with, however if you have a method which may throw an exception, also provide where possible a method to assert whether the data is actually valid before the method is called.

For example, System.IO.File.OpenRead() will throw a FileNotFoundException if the file supplied does not exist, however it also provides a .Exists() method which returns a boolean value indicating whether the file is present which you should call before calling OpenRead() to avoid any unexpected exceptions.

To answer the "when should I deal with an exception" part of the question, I would say wherever you can actually do something about it. If your method cannot deal with an exception thrown by a method it calls, don't catch it. Let it raise higher up the call chain to something that can deal with it. In some cases, this may just be a logger listening to Application.UnhandledException.

3
  • Many people, especially python programmers, prefer EAFP i.e. "It's easier to ask for forgiveness than it is to get permission"
    – Mark Booth
    Feb 16, 2012 at 13:05
  • 1
    +1 for comment about avoiding exceptions as with .Exists(). Feb 22, 2012 at 19:50
  • +1 This is still good advice. In code I write / manage, an Exception is "Exceptional", 9/10 times an Exception is intended for a developer to see, it says hey, you should be defensivley programming! The other 1 time, it is something we cannot deal with, and we log it, and exit as best we can. Consitency is important, for example, by convention we would normally have a true false reponse, and internal messages for standard fare / processing. 3rd party api's that seem to throw exceptions for everything can be handled at call, and returned using the standard agreed process. Mar 30, 2015 at 13:14
3

use a try/catch/finally to return an enum (or an int that represents a value, 0 for error, 1 for ok, 2 for warning etc, depending on the case) so that an answer is always in order,

That's a terrible design. Don't "mask" an exception by translating to a numeric code. Leave it as a proper, unambiguous exception.

or should one let the exception go through so that the calling part would deal with it?

That's what exceptions are for.

dealt with as close to where it is raised as possible

Is not a universal truth at all. It's a good idea some times. Other times it's not as helpful.

4
  • It's not a terrible design. Microsoft implements it in many places, namely on the default asp.NET Membership provider. Other than that I can't see how this answer contributes anything to the conversation Feb 17, 2012 at 16:02
  • @MihalisBagos: All I can do is suggest that Microsoft's approach is not embraced by every programming language. In languages without exceptions, returning a value is essential. C is the most notable example. In languages with exceptions, returning "code values" to indicate errors is a terrible design. It leads to (sometimes) cumbersome if statements instead of (sometimes) simpler exception handling. The answer ("How to you choose...") is simple. Don't. I think saying this adds to the conversation. You're free to ignore this answer, however.
    – S.Lott
    Feb 17, 2012 at 16:38
  • I am not saying your opinion doesn't count but I am saying your opinion is not developed. With that comment, I take it the reasoning is that where we can use exceptions, we should, just because we can? I don't see the status code as masking, rather than a categorization/organization of the code flow cases Feb 17, 2012 at 16:44
  • 1
    The Exception already is a categorization/organization. I don't see the value in replacing an unambiguous exception with a return value that can easily be confused with "normal" or "non-exceptional" return values. Return values should always be non-exceptional. My opinion is not very developed: it's very simple. Don't transform an exception into a numeric code. It was already a perfectly good data object which serves all the perfectly good use cases we can imagine.
    – S.Lott
    Feb 17, 2012 at 18:28
3

I agree with S.Lott. Catching the exception as close as possible to the source may be a good idea or a bad idea depending on the situation. The key to handling exceptions is to only catch them when you can do something about it. Catching them and returning a numeric value to the calling function is generally a bad design. You just want to let them float up until you can recover.

I always consider exception handling to be a step away from my application logic. I ask myself, If this exception is thrown how far back up the call stack do I have to crawl before my application is in a recoverable state? In a lot of cases, if there isn't anything I can do within the application to recover, that might mean I don't catch it until the top level and just log the exception, fail the job and try to shut down cleanly.

There is really no hard and fast rule to when and how to set up exception handling other than leave them alone until you know what to do with them.

1

Exceptions are beautiful things. They allow you to produce a clear description of a run time problem without resorting to unnecessary ambiguity. Exceptions can be typed, sub-typed, and may be handled by type. They can be passed around for handling elsewhere, and if they cannot be handled they can be re-raised to be dealt with at a higher layer in your application. They will also automatically return from your method without needing to invoke lots of crazy logic to deal with obfuscated error codes.

You should throw an exception immediately after encountering invalid data in your code. You should wrap calls to other methods in a try..catch..finally to handle any exceptions that might be thrown, and if you don't know how to respond to any given exception, you throw it again to indicate to higher layers that there is something wrong that should be handled elsewhere.

Managing error codes can be very difficult. You usually end up with lots of unnecessary duplication in your code, and/or lots of messy logic to deal with error states. Being a user and encountering an error code is even worse, as the code itself won't be meanful, and won't provide the user with a context for the error. An exception on the other hand can tell the user something useful, like "You forgot to enter a value", or "you entered an invalid value, here is the valid range you may use...", or "I don't know what happened, contact tech support and tell them that I just crashed, and give them the following stack trace...".

So my question to the OP is why on Earth would you NOT want to use exceptions over returning error codes?

0

All good answers. I would also like to add that returning an error code instead of throwing an exception can make the caller's code more complicated. Say method A calls method B calls method C and C encounters an error. If C returns an error code, now B needs to have logic to determine if it can handle that error code. If it can't then it need to return it to A. If A can't handle the error then what do you do? Throw an exception? In this example, the code is much cleaner if C simply throws an exception, B doesn't catch the exception so it automatically aborts without any extra code needed to do so and A can catch certain types of exceptions while letting others continue up the call stack.

This brings to mind a good rule to code by:

Lines of code are like golden bullets. You want to use as few as possible to get the job done.

0

I used a combination of both solutions: for each validation function, I pass a record that I fill with the validation status (an error code). At the end of the function, if a validation error exists, I throw an exception, this way I do not throw an exception for each field, but only once.

I also took advantage that throwing an exception will stop execution because I do not want the execution to continue when data is invalid.

For example

procedure Validate(var R:TValidationRecord);
begin
  if Field1 is not valid then
  begin
    R.Field1ErrorCode=SomeErrorCode;
    ErrorFlag := True; 
  end; 
  if Field2 is not valid then
  begin
    R.Field2ErrorCode=SomeErrorCode;
    ErrorFlag := True; 
  end;
  if Field3 is not valid then
  begin
    R.Field3ErrorCode=SomeErrorCode;
    ErrorFlag := True; 
  end;

  if ErrorFlag then
    ThrowException
end;

If relying on boolean only, the developer using my function should take this into account writing:

if not Validate() then
  DoNotContinue();

but he may forgot and only call Validate() (I know that he should not, but maybe he might).

So, in the code above I gained the two advantages:

  1. Only one exception in the validation function.
  2. Exception, even uncaught, will stop the execution, and appear at test time
0

There isn't one answer here -- kind of like there isn't one sort of HttpException.

It makes alot of sense that the underlying HTTP libraries throw an exception when they get a 4xx or 5xx response; last time I looked at the HTTP specifications those were errors.

As for throwing that exception -- or wrapping it and rethrowing -- I think that really is a question of use case. For example, if you are writing a wrapper to grab some data from the API and expose it to applications you could decide that semantically a request for a non-existent resource that returns a HTTP 404 would make more sense to catch that and return null. On the other hand a 406 error (not acceptable) might be worth throwing an error as that means something has changed and the app should be crashing and burning and screaming for help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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