363

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;
    }
}
shA.t
  • 16,580
  • 5
  • 54
  • 111
skolima
  • 31,963
  • 27
  • 115
  • 151
  • 1
    There is another way to do this that doesn't require any voodoo. Take a look at the answer here: http://stackoverflow.com/questions/15668334/preserving-exceptions-from-dynamically-invoked-methods – Timothy Shields Mar 29 '13 at 21:41
  • The exception thrown in the dynamically called method is the inner exception of the "Exception has been thrown by the target of an invocation" exception. It has its own stack trace. There really is not much else there to worry about. – ajeh Feb 12 '18 at 18:26
  • 1
    use mi.Invoke(this, BindingFlags.DoNotWrapExceptions, null, null, null) – Wouter Mar 25 '21 at 07:57

12 Answers12

577

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.

Paul Turner
  • 38,949
  • 15
  • 102
  • 166
  • 17
    Good candidate for an Exception.Rethrow() extension method? – nmarler Apr 07 '14 at 15:48
  • 8
    Note that the ExceptionDispatchInfo class is in the System.Runtime.ExceptionServices namespace, and is not available prior to .NET 4.5. – yoyo May 13 '14 at 04:25
  • 70
    You may need to put a regular `throw;` after the .Throw() line, because the compiler won't know that .Throw() always throws an exception. `throw;` will never be called as a result, but at least the compiler won't complain if your method requires a return object or is an async function. – Kind Contributor Jul 09 '14 at 23:19
  • 1
    Is there an implementation for .Net 4.0? – Luke Narramore Sep 04 '14 at 08:57
  • 1
    Why is a simple `throw;` not enough? I just tested it in a small console app and found the stack trace was not changed between rethrowing and not handling the exception at all. – Taudris Dec 02 '14 at 23:40
  • 7
    @Taudris This question is specifically about rethrowing the inner exception, which cannot be specially handled by `throw;`. If you use `throw ex.InnerException;` the stack-trace is re-initialized at the point it is re-thrown. – Paul Turner Dec 03 '14 at 00:48
  • Oops, missed that `InnerException` detail. Never mind, makes sense! This could be quite useful. – Taudris Dec 03 '14 at 01:10
  • sometimes `InnerException` is null. What in that case? – amit jha Apr 05 '16 at 07:14
  • @amitjha in case `InnerException` is null, the outer exception should just be thrown. – Peter Lillevold Apr 26 '16 at 22:52
  • 7
    @amitjha `ExceptionDispatchInfo.Capture(ex.InnerException ?? ex).Throw();` – Vedran Jun 02 '16 at 14:10
  • 1
    `throw;` **is the same** that `System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex).Throw();` and ***preserves the stacktrace*** ? – Kiquenet May 25 '18 at 10:49
  • 1
    `public static T Rethrow(this System.Exception ex){ ExceptionDispatchInfo.Capture(ex.InnerException ?? ex).Throw();throw new System.Exception(); // noop}` This allows to return to make compile happy. Not sure if its a great idea though.. – Red Riding Hood May 19 '19 at 11:45
  • @RedRidingHood, Nice but no need for Generic, right? Just void. – crokusek Feb 24 '20 at 23:48
88

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 ;
}
Anthony Mastrean
  • 21,850
  • 21
  • 110
  • 188
Anton Tykhyy
  • 19,370
  • 5
  • 54
  • 56
  • Looks cool, what needs to happen after running these functions? – vdboor Feb 23 '10 at 16:34
  • @vdboor: I don't quite understand your question. Did the edit clarify things? – Anton Tykhyy Feb 24 '10 at 07:52
  • 2
    Actually, it's not much slower than invoking `InternalPreserveStackTrace` (about 6% slower with 10000 iterations). Accessing the fields directly by reflection is about 2.5% faster than invoking `InternalPreserveStackTrace` – Thomas Levesque Jun 02 '10 at 08:58
  • @Thomas: when using `InternalPreserveStackTrace`, it is much faster to create a delegate for it instead of calling with reflection every time. I compared to the former scenario. – Anton Tykhyy Jun 24 '10 at 06:23
  • Hi, Thanks for the great code and explanations. Is it possible to add a string to the exception message so I will be able to capture not just the stack trace from where it was thrown but also the reason? – Ido Ran Nov 17 '10 at 08:44
  • 1
    I would recommend using the `e.Data` dictionary with a string or a unique object key (`static readonly object myExceptionDataKey = new object ()`, but don't do this if you have to serialize exceptions anywhere). Avoid modifying `e.Message`, because you might have code somewhere which parses `e.Message`. Parsing `e.Message` is evil, but there may be no other choice, e.g. if you have to use a 3rd-party library with poor exception practices. – Anton Tykhyy Nov 17 '10 at 12:48
  • 10
    DoFixups breaks for custom exceptions if they do not have the serialization ctor – ruslander Feb 16 '12 at 19:06
  • 3
    The suggested solution doesn't work if the exception doesn't have a serialization constructor. I suggest to use the solution proposed at http://stackoverflow.com/a/4557183/209727 that work well in any case. For .NET 4.5 consider to use ExceptionDispatchInfo class. – Davide Icardi Jan 27 '13 at 23:40
  • So it is. I mention in my answer that the advantage of the serialization approach lies in relying only on public APIs. Apparently some code review standards frown on using reflection to access CLR internals. As for the `ExceptionDispatchInfo` class, it's nice but not serializable (probably because it preserves Watson information in addition to the stack trace), which limits its usefulness somewhat. – Anton Tykhyy Jan 28 '13 at 01:14
  • The answer https://stackoverflow.com/a/9989557/206730 ***not require*** the exception type to be ***serializable*** ? – Kiquenet May 25 '18 at 10:17
36

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

throw;

And then extract the innerexception later.

GEOCHET
  • 21,119
  • 15
  • 74
  • 98
  • 23
    Or remove the try/catch altogether. – Daniel Earwicker Dec 17 '08 at 22:41
  • 11
    @Earwicker. Removing the try/catch is not a good solution in general as it ignores cases where cleanup code is required prior to propagating the exception up the call stack. – Jordan Nov 02 '09 at 20:43
  • 17
    @Jordan - Clean up code should be in a finally block not a catch block – Paolo Jan 02 '10 at 18:12
  • 23
    @Paolo - If it is supposed to be executed in every case, yes. If it is supposed to be executed only in failure case, no. – chiccodoro Sep 01 '10 at 15:03
  • 4
    Keep in mind that InternalPreserveStackTrace isnt thread safe, so if you have 2 threads in on of these exception states... may god have mercy on us all. – Rob Feb 04 '11 at 01:35
  • 2
    This works if you "throw" immediately in the catch block, but it doesn't work if you need to "throw" outside a catch block. – Mark Lakata Oct 14 '11 at 20:16
  • @DanielEarwicker there are cases where you want to execute code in the catch, and then rethrow. Another case is when you want to rethrow a particular type of exception but catch locally anything else. – Teejay Jul 28 '23 at 13:43
31

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:

1.

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.

Mark
  • 577
  • 5
  • 5
  • 7
    I always thought that 'throw' didn't reset the stacktrace (as opposed to 'throw e'). – Jesper Matthiesen Oct 19 '17 at 13:31
  • @JesperMatthiesen I might be mistaken, but I heard that it depends if the exception was thrown and caught in the same file. If it's the same file, the stack trace will be lost, if it's another file, it will be preserved. – jahu Oct 01 '19 at 10:26
  • What I can find on `ExceptionDispatchInfo` points to its use when you want to re-throw outside the context of the catch, or to unwrap an `AggregateException`. The difference I see in my tests is that `throw;` fails to capture any lines from the `try` in the stacktrace. Changing example 3 to `try { DoThrow(); } catch (Exception) { throw; }`, you'll see that the throwing line in `DoThrow` *is* included in the stacktrace, but still not the calling line from the `try`. Whereas `ExceptionDispatchInfo.Capture` includes that line. Still seems like too much work outside of the primary use-cases. – Johann Sep 09 '20 at 01:17
  • 5
    The behavior from example 3 was logged as a bug against .NET Core, and fixed in .NET Core 2.1: https://github.com/dotnet/runtime/issues/9518 – Johann Sep 09 '20 at 01:23
15
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.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
Eric
  • 151
  • 1
  • 2
  • Be aware that in .Net 4.0, InternalPreserveStackTrace is now a no-op - look in Reflector and you'll see the method is completely empty! – Samuel Jack Apr 19 '10 at 14:37
  • Scratch that: I was looking at the RC: in the beta, they've put the implementation back again! – Samuel Jack Apr 19 '10 at 20:04
  • 3
    suggestion: change PreserveStackTrace to return ex - then to throw an exception you can just say: throw ex.PreserveStackTrace(); – Simon_Weaver Nov 09 '10 at 05:33
  • Why use `Action` ? [Here](https://stackoverflow.com/a/4821722/206730) use **static method** – Kiquenet May 25 '18 at 10:14
12

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();
        }
    }
Jürgen Steinblock
  • 30,746
  • 24
  • 119
  • 189
11

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.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
skolima
  • 31,963
  • 27
  • 115
  • 151
  • 28
    This is a really, really bad idea, as it depends on internal undocumented details about framework classes. – Daniel Earwicker Dec 17 '08 at 22:39
  • 1
    Turns out it's possible to preserve the stack trace without Reflection, see below. – Anton Tykhyy Jan 19 '10 at 23:23
  • 1
    Calling the internal `InternalPreserveStackTrace` method would be better, since it does the same thing and is less likely to change in the future... – Thomas Levesque Jun 02 '10 at 08:54
  • 1
    Actually, it would be worse, as InternalPreserveStackTrace does not exist on Mono. – skolima Jun 07 '10 at 16:15
  • 5
    @daniel - well its a really, really, really bad idea for throw; to reset the stacktrace when every .net developer is trained to believe it won't. its also a really, really, really bad thing if you can't find out the source of a NullReferenceException and lose a customer/order because you can't find it. for me that trumps 'undocumented details' and definitely mono. – Simon_Weaver Nov 09 '10 at 05:28
  • 1
    @Simon: `throw;` doesn't reset the stack trace. `throw e;` does. – Anton Tykhyy Nov 18 '10 at 10:47
  • Yeah, this is bad. Yeah, it's a hack. Yeah, it might break. But write a unit test for it, and if it breaks in some future update to .NET you will find it before you release to customers, and all you'll be out is time in the future working up a new hack. – tster Dec 12 '17 at 01:14
11

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.

Siyual
  • 16,415
  • 8
  • 44
  • 58
kokos
  • 43,096
  • 5
  • 36
  • 32
6

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))();

    }
Boris Treukhov
  • 17,493
  • 9
  • 70
  • 91
3

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 });
    }
2

This is just a nice clean, modern implementation of some of the other ideas here, tested in .NET 6:

public static class ExceptionExtensions
{
    [DoesNotReturn]
    public static void Rethrow(this Exception ex) 
        => ExceptionDispatchInfo.Capture(ex).Throw();
}

I wanted the value of the PropertyName property on myObject but this will work just as well when using reflection to call methods (as per OP's problem) or anything else that results in you wanting to re-throw an inner exception.

try
{
    object? value = myObject.GetType().GetProperty("PropertyName")?.GetValue(myObject);
}
catch (TargetInvocationException ex)
{
    (ex.InnerException ?? ex).Rethrow();
}
Ben
  • 5,525
  • 8
  • 42
  • 66
0

Here is a combination of practices on how to unpack and rethrow the original exceptions in the AggregateException combined with the ExceptionDispatchInfo class.

using ExceptionDispatchInfo = 
    System.Runtime.ExceptionServices.ExceptionDispatchInfo;

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

A note on throw; This is also considered best practice after using the ExceptionDispatchInfo so the compiler doesn't complain about methods which don't either return or throw. Although its expected to never be reached actually.

I spend the past weeks getting my head around the concept of 'correctly' rethrowing with the AggregateException. This is poorly documented on the microsoft provided docs (see https://learn.microsoft.com/en-us/dotnet/api/system.aggregateexception.flatten and https://learn.microsoft.com/en-us/dotnet/api/system.aggregateexception), because while they do provide some guidance on how to unpack it the practices are fragmented and it doesn't abide the latest 'rethrowing without loosing the stacktrace' practices.

Notes:

  • Using the 'normal' await task; flow is the recommended pattern in this specific instance and will solve rethrowing of the original exceptions for you.
  • The need for unpacking could be considered to be a leaky abstraction.
  • What is a "code pad"? – Enigmativity Jul 31 '23 at 10:00
  • Good point, let me rephrase it to more clearly state the problem. To quote from https://stackoverflow.com/questions/22623/best-practices-for-catching-and-re-throwing-net-exceptions 'So the compiler doesn't complain about methods which don't either return or throw.' – Erik de Roos Jul 31 '23 at 10:15