How to rethrow InnerException without losing stack trace in C#?

C#.NetException

C# Problem Overview


I am calling, through reflection, a method which may cause an exception. How can I pass the exception to my caller without the wrapper reflection puts around it?
I am rethrowing the InnerException, but this destroys the stack trace.
Example code:

public void test1()
{
    // Throw an exception for testing purposes
    throw new ArgumentException("test1");
}

void test2()
{
    try
    {
        MethodInfo mi = typeof(Program).GetMethod("test1");
        mi.Invoke(this, null);
    }
    catch (TargetInvocationException tiex)
    {
        // Throw the new exception
        throw tiex.InnerException;
    }
}

C# Solutions


Solution 1 - C#

In .NET 4.5 there is now the ExceptionDispatchInfo class.

This lets you capture an exception and re-throw it without changing the stack-trace:

using ExceptionDispatchInfo = 
    System.Runtime.ExceptionServices.ExceptionDispatchInfo;

try
{
    task.Wait();
}
catch(AggregateException ex)
{
    ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}

This works on any exception, not just AggregateException.

It was introduced due to the await C# language feature, which unwraps the inner exceptions from AggregateException instances in order to make the asynchronous language features more like the synchronous language features.

Solution 2 - C#

It is possible to preserve the stack trace before rethrowing without reflection:

static void PreserveStackTrace (Exception e)
{
    var ctx = new StreamingContext  (StreamingContextStates.CrossAppDomain) ;
    var mgr = new ObjectManager     (null, ctx) ;
    var si  = new SerializationInfo (e.GetType (), new FormatterConverter ()) ;

    e.GetObjectData    (si, ctx)  ;
    mgr.RegisterObject (e, 1, si) ; // prepare for SetObjectData
    mgr.DoFixups       ()         ; // ObjectManager calls SetObjectData

    // voila, e is unmodified save for _remoteStackTraceString
}

This wastes a lot of cycles compared to calling InternalPreserveStackTrace via cached delegate, but has the advantage of relying only on public functionality. Here are a couple of common usage patterns for stack-trace preserving functions:

// usage (A): cross-thread invoke, messaging, custom task schedulers etc.
catch (Exception e)
{
    PreserveStackTrace (e) ;

    // store exception to be re-thrown later,
    // possibly in a different thread
    operationResult.Exception = e ;
}

// usage (B): after calling MethodInfo.Invoke() and the like
catch (TargetInvocationException tiex)
{
    PreserveStackTrace (tiex.InnerException) ;

    // unwrap TargetInvocationException, so that typed catch clauses 
    // in library/3rd-party code can work correctly;
    // new stack trace is appended to existing one
    throw tiex.InnerException ;
}

Solution 3 - C#

I think your best bet would be to just put this in your catch block:

throw;

And then extract the innerexception later.

Solution 4 - C#

Nobody has explained the difference between ExceptionDispatchInfo.Capture( ex ).Throw() and a plain throw, so here it is.

The complete way to rethrow a caught exception is to use ExceptionDispatchInfo.Capture( ex ).Throw() (only available from .Net 4.5).

Below there are the cases necessary to test this:

void CallingMethod()
{
	//try
	{
		throw new Exception( "TEST" );
	}
	//catch
	{
	//    throw;
	}
}

2.

void CallingMethod()
{
	try
	{
		throw new Exception( "TEST" );
	}
	catch( Exception ex )
	{
		ExceptionDispatchInfo.Capture( ex ).Throw();
		throw; // So the compiler doesn't complain about methods which don't either return or throw.
	}
}

3.

void CallingMethod()
{
	try
	{
		throw new Exception( "TEST" );
	}
	catch
	{
		throw;
	}
}

4.

void CallingMethod()
{
	try
	{
		throw new Exception( "TEST" );
	}
	catch( Exception ex )
	{
		throw new Exception( "RETHROW", ex );
	}
}

Case 1 and case 2 will give you a stack trace where the source code line number for the CallingMethod method is the line number of the throw new Exception( "TEST" ) line.

However, case 3 will give you a stack trace where the source code line number for the CallingMethod method is the line number of the throw call. This means that if the throw new Exception( "TEST" ) line is surrounded by other operations, you have no idea at which line number the exception was actually thrown.

Case 4 is similar with case 2 because the line number of the original exception is preserved, but is not a real rethrow because it changes the type of the original exception.

Solution 5 - C#

public static class ExceptionHelper
{
    private static Action<Exception> _preserveInternalException;

    static ExceptionHelper()
    {
        MethodInfo preserveStackTrace = typeof( Exception ).GetMethod( "InternalPreserveStackTrace", BindingFlags.Instance | BindingFlags.NonPublic );
        _preserveInternalException = (Action<Exception>)Delegate.CreateDelegate( typeof( Action<Exception> ), preserveStackTrace );            
    }

    public static void PreserveStackTrace( this Exception ex )
    {
        _preserveInternalException( ex );
    }
}

Call the extension method on your exception before you throw it, it will preserve the original stack trace.

Solution 6 - C#

Based on Paul Turners answer I made an extension method

    public static Exception Capture(this Exception ex)
    {
        ExceptionDispatchInfo.Capture(ex).Throw();
        return ex;
    }

the return ex ist never reached but the advantage is that I can use throw ex.Capture() as a one liner so the compiler won't raise an not all code paths return a value error.

    public static object InvokeEx(this MethodInfo method, object obj, object[] parameters)
    {
        {
            return method.Invoke(obj, parameters);
        }
        catch (TargetInvocationException ex) when (ex.InnerException != null)
        {
            throw ex.InnerException.Capture();
        }
    }

Solution 7 - C#

Even more reflection...

catch (TargetInvocationException tiex)
{
    // Get the _remoteStackTraceString of the Exception class
    FieldInfo remoteStackTraceString = typeof(Exception)
        .GetField("_remoteStackTraceString",
            BindingFlags.Instance | BindingFlags.NonPublic); // MS.Net

    if (remoteStackTraceString == null)
        remoteStackTraceString = typeof(Exception)
        .GetField("remote_stack_trace",
            BindingFlags.Instance | BindingFlags.NonPublic); // Mono

    // Set the InnerException._remoteStackTraceString
    // to the current InnerException.StackTrace
    remoteStackTraceString.SetValue(tiex.InnerException,
        tiex.InnerException.StackTrace + Environment.NewLine);

    // Throw the new exception
    throw tiex.InnerException;
}

Keep in mind that this may break at any time, as private fields are not part of API. See further discussion on Mono bugzilla.

Solution 8 - C#

First: don't lose the TargetInvocationException - it's valuable information when you will want to debug things.
Second: Wrap the TIE as InnerException in your own exception type and put an OriginalException property that links to what you need (and keep the entire callstack intact).
Third: Let the TIE bubble out of your method.

Solution 9 - C#

Guys, you are cool.. I'm gonna be a necromancer soon.

    public void test1()
    {
        // Throw an exception for testing purposes
        throw new ArgumentException("test1");
    }

    void test2()
    {
            MethodInfo mi = typeof(Program).GetMethod("test1");
            ((Action)Delegate.CreateDelegate(typeof(Action), mi))();

    }

Solution 10 - C#

Anpother sample code which uses exception serialization/deserialization. It does not require the actual exception type to be serializable. Also it uses only public/protected methods.

    static void PreserveStackTrace(Exception e)
    {
        var ctx = new StreamingContext(StreamingContextStates.CrossAppDomain);
        var si = new SerializationInfo(typeof(Exception), new FormatterConverter());
        var ctor = typeof(Exception).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }, null);

        e.GetObjectData(si, ctx);
        ctor.Invoke(e, new object[] { si, ctx });
    }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionskolimaView Question on Stackoverflow
Solution 1 - C#Paul TurnerView Answer on Stackoverflow
Solution 2 - C#Anton TykhyyView Answer on Stackoverflow
Solution 3 - C#GEOCHETView Answer on Stackoverflow
Solution 4 - C#jeuoekdcwzfwccuView Answer on Stackoverflow
Solution 5 - C#EricView Answer on Stackoverflow
Solution 6 - C#Jürgen SteinblockView Answer on Stackoverflow
Solution 7 - C#skolimaView Answer on Stackoverflow
Solution 8 - C#kokosView Answer on Stackoverflow
Solution 9 - C#Boris TreukhovView Answer on Stackoverflow
Solution 10 - C#chickenbyproductView Answer on Stackoverflow