Catch/Modify (Message)/Rethrow Exception of same type

Viewed 8297

I want a central place to extract information from an exception, set all the information I need to its message parameter and then rethrow that information as an Exception of the same type.

The better solution probably would be to do this at the place where the exception is finally being handled (and its message logged), but.. I have control over the place throwing the exception, and not over the place that receives the exception and only logs its Message content.

Apart from that design decision and given that message is a readonly property, I would have (?) to create a new Exception object in some way, is there a way to make the new exception object the same type as the original one?

Here is my code, which does not compile - it stumbles over the throw line (where I try to dynamically cast the object).

public static void RethrowExceptionWithFullDetailInMessage(string msg, Exception ex)
{
    Exception curEx = ex;
    int cnt = 0;
    while (curEx != null)
    {
        msg += "\r\n";
        msg += cnt++ + " ex.message: " + curEx.Message + "\r\n";
        msg += "Stack: " + curEx.StackTrace;
        curEx = curEx.InnerException;
    }
    object newEx = Convert.ChangeType(new Exception(msg), ex.GetType());
    throw (ex.GetType())newEx;
}

Does this

throw (Exception)newEx;

preserve the type? (It compiles.)

Does the Convert.ChangeType make sure I get an Exception of the correct type?

5 Answers

You can change the exception message via reflection like this...

Exception exception = new Exception("Some message.");
var type = typeof(Exception);
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
var fieldInfo = type.GetField("_message", flags);
fieldInfo.SetValue(exception, message);

So you can create an extension method...

namespace ExceptionExample
{
    public static class ExceptionExtensions
    {
        public static void SetMessage(this Exception exception, string message)
        {
            if (exception == null)
                throw new ArgumentNullException(nameof(exception));

            var type = typeof(Exception);
            var flags = BindingFlags.Instance | BindingFlags.NonPublic;
            var fieldInfo = type.GetField("_message", flags);
            fieldInfo.SetValue(exception, message);
        }
    }
}

And then use it...

...
using static ExceptionExample.ExceptionExtensions;

public class SomeClass
{
    public void SomeMethod()
    {
        var reader = AnotherClass.GetReader();
        try
        {
            reader.Read();
        }
        catch (Exception ex)
        {
            var connection = reader?.Connection;
            ex.SetMessage($"The exception message was replaced.\n\nOriginal message: {ex.Message}\n\nDatabase: {connection?.Database}");
            throw; // you will not lose the stack trace
        }
    }
}

You have to keep in mind that if you use "throw ex;" the stack trace will be lost.

To avoid this you must use "throw;" without the exception.

Supplemental comment.

These all work in supplementing the exception message, but I found that using "throw" did NOT preserve the StackTrace - the last trace pointed to the actual "throw" statement (dropping the root cause location).

From discussions elsewhere, it's clear there are some circumstances that throw doesn't preserve due to CLR stack limitations

Throw and preserve stack trace not as expected as described by Code Analysis

Solution: dump the StackTrace in each exception (e.g. and add to the error message) and/or dump to logging

Related