46

This question is intended to apply to any OO programming language that supports exception handling; I am using C# for illustrative purposes only.

Exceptions are usually intended to be raised when an problem arises that the code cannot immediately handle, and then to be caught in a catch clause in a different location (usually an outer stack frame).

Q: Are there any legitimate situations where exceptions are not thrown and caught, but simply returned from a method and then passed around as error objects?

This question came up for me because .NET 4's System.IObserver<T>.OnError method suggests just that: exceptions being passed around as error objects.

Let's look at another scenario, validation. Let's say I am following conventional wisdom, and that I am therefore distinguishing between an error object type IValidationError and a separate exception type ValidationException that is used to report unexpected errors:

partial interface IValidationError { }

abstract partial class ValidationException : System.Exception
{
    public abstract IValidationError[] ValidationErrors { get; }
}

(The System.Component.DataAnnotations namespace does something quite similar.)

These types could be employed as follows:

partial interface IFoo { }  // an immutable type

partial interface IFooBuilder  // mutable counterpart to prepare instances of above type
{
    bool IsValid(out IValidationError[] validationErrors);  // true if no validation error occurs
    IFoo Build();  // throws ValidationException if !IsValid(…)
}

Now I am wondering, could I not simplify the above to this:

partial class ValidationError : System.Exception { }  // = IValidationError + ValidationException

partial interface IFoo { }  // (unchanged)

partial interface IFooBuilder
{
    bool IsValid(out ValidationError[] validationErrors);
    IFoo Build();  // may throw ValidationError or sth. like AggregateException<ValidationError>
}

Q: What are the advantages and disadvantages of these two differing approaches?

7
  • If you're looking for two separate answers, you should ask two separate questions. Combining them just makes it that much harder to answer.
    – Bobson
    Commented Aug 5, 2013 at 13:18
  • 2
    @Bobson: I see these two questions as complementary: The former question is supposed to define the general context of the issue in broad terms, while the latter asks for specific information that should allow readers to draw their own conclusion. An answer doesn't have to give separate, explicit answers to both questions; answers "in-between" are just as welcome.
    – stakx
    Commented Aug 5, 2013 at 13:51
  • 5
    I'm unclear on something: What is the reason for deriving ValidationError from Exception, if you're not going to throw it?
    – pdr
    Commented Aug 5, 2013 at 14:06
  • @pdr: Do you mean in the case of throwing AggregateException instead? Good point.
    – stakx
    Commented Aug 5, 2013 at 15:27
  • 3
    Not really. I mean that if you're going to throw it, use an exception. If you're going to return it, use an error object. If you have expected exceptions that are really errors by nature, catch them and put them into your error object. Whether or not either one allows for multiple errors is entirely irrelevant.
    – pdr
    Commented Aug 5, 2013 at 17:34

8 Answers 8

31

Are there any legitimate situations where exceptions are not thrown and caught, but simply returned from a method and then passed around as error objects?

If it is never thrown then it's not an exception. It is an object derived from an Exception class, though it does not follow the behavior. Calling it an Exception is purely semantics at this point, but I see nothing wrong with not throwing it. From my perspective, not throwing an exception is the exact same thing as an inner-function catching it and preventing it from propagating.

Is it valid for a function to return exception objects?

Absolutely. Here is a short list of examples where this may be appropriate:

  • An exception factory.
  • A context object that reports if there was a previous error as a ready to use exception.
  • A function that keeps a previously caught exception.
  • A third-party API that creates an exception of an inner type.

Is not throwing it bad?

Throwing an exception is kind of like: "Go to jail. Do not pass Go!" in the board game Monopoly. It tells a compiler to jump over all source code up to the catch without executing any of that source code. It doesn't have anything to do with errors, reporting or stopping bugs. I like to think of throwing an exception as a "super return" statement for a function. It returns execution of the code to somewhere much higher up than the original caller.

The important thing here is to understand that the true value of exceptions is in the try/catch pattern, and not the instantiation of the exception object. The exception object is just a state message.

In your question you seem to be confusing the usage of these two things: a jumping to a handler of the exception, and the error state the exception represents. Just because you took an error state and wrapped it in an exception does not mean you are following the try/catch pattern or its benefits.

3
  • 4
    "If it is never thrown then it's not an exception. It is an object derived from an Exception class but does not follow the behavior." -- And that is exactly the issue: Is it legitimate to use an Exception-derived object in a situation where it won't get thrown at all, neither by the producer of the object, nor by any calling method; where it only serves as a "state message" being passed around, without the "super return" control flow? Or am I violating an unspoken try/catch(Exception) contract so badly that I should never do this, and instead use a separate, non-Exception type?
    – stakx
    Commented Aug 5, 2013 at 21:09
  • 10
    @stakx programmers have and will continue to do things that are confusing to other programmers, but not technically incorrect. That's why I said it was semantics. The legal answer is No you are not breaking any contracts. The popular opinion would be that you shouldn't do that. Programming is full of examples where your source code does nothing wrong but everyone dislikes it. Source code should always be written so that it's clear what the intent is. Are you really helping another programmer by using an exception that way? If yes, then do so. Otherwise don't be surprised if it's disliked.
    – Reactgular
    Commented Aug 5, 2013 at 22:27
  • @cgTag Your use of inline code blocks for italics makes my brain hurt..
    – Frayt
    Commented Oct 18, 2018 at 12:14
51

Returning exceptions instead of throwing them can make semantical sense when you have a helper-method for analyzing the situation and returning an appropriate exception which is then thrown by the caller (you could call this an "exception factory"). Throwing an exception in this error analyzer function would mean that something went wrong during the analysis itself, while returning an exception means that the kind of error was analyzed succesfully.

One possible use-case could be a function which turns HTTP response codes into exceptions:

Exception analyzeHttpError(int errorCode) {
    if (errorCode < 400) {
         throw new NotAnErrorException();
    }
    switch (errorCode) {
        case 403:
             return new ForbiddenException();
        case 404:
             return new NotFoundException();
        case 500:
             return new InternalServerErrorException();
        …
        default:
             throw new UnknownHttpErrorCodeException(errorCode);
     }
}

Note that throwing an exception means that the method was used wrong or had an internal error, while returning an exception means that the error code was identified succesfully.

6
  • This also helps the compiler understand the code flow: if a method never correctly returns (because it throws the "desired" exception) and the compiler doesn't know about that, then you might sometimes need superfluous return statements to make the compiler stop complaining. Commented Aug 5, 2013 at 16:01
  • @JoachimSauer Yeah. More than once I wish they would add a return type of "never" to C#--it would indicate the function must exit by throwing, putting a return in it is an error. Sometimes you have code whose only job is prettying up an exception. Commented Aug 5, 2013 at 22:28
  • 2
    @LorenPechtel A function that never returns is not a function. It's a terminator. Call a function like that clears the calling stack. It's similar to calling System.Exit(). The code after the function call is not executed. That doesn't sound like a good design pattern for a function.
    – Reactgular
    Commented Aug 5, 2013 at 23:49
  • 5
    @MathewFoscarini Consider a class that has multiple methods that can error in similar ways. You want to dump some state information as part of the exception to indicate what it was chewing on when it went boom. You want one copy of the prettying-up code because of DRY. That leaves either calling a function that does the prettying-up and using the result in your throw or it means a function that builds the prettied-up text and then throws it. I generally find the latter superior. Commented Aug 6, 2013 at 3:27
  • 1
    @LorenPechtel ah, yeah I've done that too. Okay I understand now.
    – Reactgular
    Commented Aug 6, 2013 at 11:14
5

Conceptually, if the exception object is the expected result of the operation, then yes. The cases I can think of, however, always involve throw-catch at some point:

  • Variations on the "Try" pattern (where a method encapsulates a second exception-throwing method, but catches the exception and instead returns a boolean indicating success). You could, instead of returning a Boolean, return any Exception thrown (null if it succeeded), allowing the end user more information while retaining the catch-less success or failure indication.

  • Workflow error processing. Similar in practice to "try pattern" method encapsulation, you might have a workflow step abstracted in a chain-of-command pattern. If a link in the chain throws an exception, it's often cleaner to catch that exception in the abstracting object, then return it as a result of said operation so the workflow engine and the actual code run as a workflow step don't require extensive try-catch logic of their own.

1
  • I don't think seen any notable people advocate such a variation on the "Try" pattern, but the "recommended" approach of returning a Boolean seems pretty anemic. I would think it might better to have an abstract OperationResult class, with a bool property "Successful", a GetExceptionIfAny method, and an AssertSuccessful method (which would throw the exception from GetExceptionIfAny if non-null). Having GetExceptionIfAny be a method rather than a property would allow the use of static immutable error objects for cases where there wasn't much useful to say.
    – supercat
    Commented Jul 9, 2014 at 20:35
5

Lets say you enqued some task to be executed in some thread pool. If this task throw exception, it is different thread so you don't catch it. Thread where it was executed just die.

Now consider that something (either code in that task, or the thread pool implementation) catches that exception store it along with task and consider task finished (unsucessfully). Now you can ask whether task is finished (and either ask whether it threw, or it can be thrown again in your thread (or better, new exception with original as cause)).

If you do it manually, you can notice that you are creating new exception, throwing it and catching it, then storing it, in different thread retrieving throwing, catching and reacting on it. So it can make sense to skip throw and catch and just store it and finish task and then just ask whether there is exception and react on it. But this can lead in more complicated code if in the same place there can be really thrown exceptions.

PS: this is written with experience with Java, where stack trace information is created when exception is created, (unlike C# where it is created when throwing). So not thrown exception approach in Java will be slower than in C# (unless its precreated and reused), but will have stack trace info available.

In general, I would stay away from creating exception and never throwing it (unless as performance optimalization after profiling point it out as bottleneck). At least in java, where exception creation is expensive (stack trace). In C# it is possibility, but IMO its surprising and thus should be avoided.

1
  • This often happens with error listener objects as well. A queue will execute the task, catch any exception, then pass that exception to the error listener responsible. It doesn't usually make sense to kill the task thread that doesn't know much about the job in this case. This sort of idea is also built into the Java APIs where one thread can get passed exceptions from another: docs.oracle.com/javase/1.5.0/docs/api/java/lang/… Commented Aug 5, 2013 at 22:18
1

It depends on your design, typically I would not return exceptions to a caller, rather I would throw and catch them and leave it at that. Typically code is written to fail early and fast. For example consider the case of opening a file and processing it (This is C# PsuedoCode):

        private static void ProcessFileFailFast()
        {
            try
            {
                using (var file = new System.IO.StreamReader("c:\\test.txt"))
                {
                    string line;
                    while ((line = file.ReadLine()) != null)
                    {
                        ProcessLine(line);
                    }
                }
            }
            catch (Exception ex) 
            {
                LogException(ex);
            }
        }

        private static void ProcessLine(string line)
        {
            //TODO:  Process Line
        }

        private static void LogException(Exception ex)
        {
            //TODO:  Log Exception
        }

In this case, we would error out and stop processing the file as soon as it encountered a bad record.

But say the requirement is we want to continue processing the file even if one or more lines has an error. The code may start to look something like this:

    private static void ProcessFileFailAndContinue()
    {
        try
        {
            using (var file = new System.IO.StreamReader("c:\\test.txt"))
            {
                string line;
                while ((line = file.ReadLine()) != null)
                {
                    Exception ex = ProcessLineReturnException(line);
                    if (ex != null)
                    {
                        _Errors.Add(ex);
                    }
                }
            }

            //Do something with _Errors Here
        }
        //Catch errors specifically around opening the file
        catch (System.IO.FileNotFoundException fnfe) 
        { 
            LogException(fnfe);
        }    
    }

    private static Exception ProcessLineReturnException(string line)
    {
        try
        {
            //TODO:  Process Line
        }
        catch (Exception ex)
        {
            LogException(ex);
            return ex;
        }

        return null;
    }

So in this case we return the exception back to the caller, although I would probably not return an exception back but instead some sort of error object since the exception has been caught and already dealt with. This is no harm with returning an exception back but other callers could re-throw the exception which may not be desireable since exception object have that behavior. If you wanted callers have the ability to re-throw then return an exception, otherwise, take the information out of the exception object and construct a smaller light weight object and return that instead. The fail fast is usually cleaner code.

For your validation example, I probably wouldn't inherit from an exception class or throw exceptions because validation errors could be quite common. It would be less overhead to return an object rather than to throw an exception if 50% of my users fail to fill out a form correctly on the first attempt.

0
1

Q: Are there any legitimate situations where exceptions are not thrown and caught, but simply returned from a method and then passed around as error objects?

Yes. For example, I recently encountered a situation situation where I found that thrown Exceptions in a ASMX Web Service donot include a element in the resultant SOAP message, so I had to generate it.

Illustrative code:

Public Sub SomeWebMethod()
    Try
        ...
    Catch ex As Exception
        Dim soapex As SoapException = Me.GenerateSoapFault(ex)
        Throw soapex
    End Try
End Sub

Private Function GenerateSoapFault(ex As Exception) As SoapException
    Dim document As XmlDocument = New XmlDocument()
    Dim faultDetails As XmlNode = document.CreateNode(XmlNodeType.Element, SoapException.DetailElementName.Name, SoapException.DetailElementName.Namespace)
    faultDetails.InnerText = ex.Message
    Dim exceptionType As String = ex.GetType().ToString()
    Dim soapex As SoapException = New SoapException("SoapException", SoapException.ClientFaultCode, Context.Request.Url.ToString, faultDetails)
    Return soapex
End Function
1

In most languages (afaik), exceptions come with some added goodies. Mostly, a snapshot of the current stack trace is stored in the object. This is nice for debugging, but can also have memory implications.

As @ThinkingMedia already said, you are really using the exception as an error object.

In your code example, it seems you mainly do this for code reuse and to avoid composition. I personally don't think this is a good reason to do this.

Another possible reason would be to trick the language to give you an error object with a stack trace. This gives additional contextual information to your error handling code.

On the other hand, we can assume that keeping the stack trace around has a memory cost. E.g. if you start collecting these objects somewhere, you may see an unpleasant memory impact. Of course this largely depends on how exception stack traces are implemented in the respective language / engine.

So, is it a good idea?

So far I have not seen it being used or recommended. But this does not mean much.

The question is: Is the stack trace really useful for your error handling? Or more generally, which information do you want to provide to the developer or administrator?

The typical throw/try/catch pattern makes it kind of necessary to have a stack trace, because otherwise you have no clue where it is coming from. For a returned object, it always comes from the called function. The stack trace might contain all the information that the developer needs, but maybe something less heavy and more specific would suffice.

-4

The answer is yes. See http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Exceptions and open up the arrow for Google's C++ style guide on exceptions. You can see there the arguments that they used to decide against, but say that if they had to do it over again, they would have likely decided for.

However it is worth noting that the Go language does not idiomatically use exceptions either, for similar reasons.

1
  • 1
    Sorry, but I don't understand how these guidelines help to answer the question: They simply recommend that exception handling be avoided completely. This is not what I'm asking about; my question is whether it is legitimate to use an exception type to describe an error condition in a situation where the resulting exception object won't ever be thrown. There appears to be nothing about that in these guidelines.
    – stakx
    Commented Aug 5, 2013 at 20:59

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