19

Duplicate of: In C#, how can I rethrow InnerException without losing stack trace?

I have some operations that I invoke asynchronously on a background thread. Sometimes, things go bad. When this happens, I tend to get a TargetInvocationException, which, while appropriate, is quite useless. What I really need is the TargetInvocationException's InnerException, like this:

    try
    {
        ReturnValue = myFunctionCall.Invoke(Target, Parameters);
    }
    catch (TargetInvocationException err)
    {
        throw err.InnerException;
    }

That way, my callers are served up with the REAL exception that occured. The problem is, that the throw statement seems to reset the stack trace. I'd like to basically rethrow the inner exception, but keep the stack trace it originally had. How do I do that?

CLARIFICATION: The reason I want only the inner exception is that this class tries to 'abstract away' the whole fact that these functions (delegates supplied by caller) are run on other threads and whatnot. If there is an exception, then odds are it has nothing to do with being run on a background thread, and the caller would really like the stack trace that goes into their delegate and finds the real issue, not my call to invoke.

Community
  • 1
  • 1
GWLlosa
  • 23,995
  • 17
  • 79
  • 116

8 Answers8

29

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 InternalPreserveStackTrace, 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 ;
}
Andrew Bullock
  • 36,616
  • 34
  • 155
  • 231
Anton Tykhyy
  • 19,370
  • 5
  • 54
  • 56
  • 1
    wasting cycles when you have an exception is worth it. So, how did you figure this out? thanks for the post! – Mark Lakata Oct 14 '11 at 20:12
  • Great answer. Please note however that `DoFixups()` [will throw SerializationExceptions](http://stackoverflow.com/questions/3086234), in case an exception class is not deserializable. You might therefore want to wrap the body of `PreserveStackTrace` in a big `try/catch`. – Evgeniy Berezovsky Oct 16 '13 at 08:09
  • Thanks. Non-serializable exceptions are generally a [bad idea](http://stackoverflow.com/questions/1066701/why-should-i-always-make-my-exceptions-serializable-net), so I think it's best to fail early here. [MSDN agrees](http://msdn.microsoft.com/query/dev11.query?appId=Dev11IDEF1&l=EN-US&k=k%28CA2237%29&rd=true#sectionToggle3). – Anton Tykhyy Oct 16 '13 at 08:27
  • I am using a library, RabbitMq's .net client to be precise, that throws non-serializable exceptions, and I'd rather not have the original exception get discarded because that library doesn't adhere to standards. So I guess it depends. That said, RabbitMq is open source, and if I contributed `Serializable` back rather than wasting time in SO comments... ;) – Evgeniy Berezovsky Oct 18 '13 at 09:46
6

No, that isn't possible. Your only real opportunity is to follow the recommended pattern and throw your own exception with the appropriate InnerException.

Edit

If your concern is the presence of the TargetInvocationException and you want to disregard it (not that I recommend this, as it could very well have something to do with the fact that it's being run on another thread) then nothing is stopping you from throwing your own exception here and attaching the InnerException from the TargetInvocationException as your own InnerException. It's a little smelly, but it might accomplish what you want.

Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
  • @Anton: That's definitely an interesting find. I don't really like the idea of being able to strip off an outer exception--even `TargetInvocationException`--but clearly there is a significant portion of the community that does. – Adam Robinson Jan 20 '10 at 01:59
  • 1
    Yes, because often there's no other way — library and third-party code seldom understands `TargetInvocationException` et al., and even in our own code handling it everywhere it might conceivably pop up is no end of a bother, what with exception type checks duplicating the catch clauses and stuff. – Anton Tykhyy Jan 20 '10 at 09:15
5

There is a way of "resetting" the stack trace on an exception by using the internal mechanism that is used to preserve server side stack traces when using remoting, but it is horrible:

try
{
    // some code that throws an exception...
}
catch (Exception exception)
{
    FieldInfo remoteStackTraceString = typeof(Exception).GetField("_remoteStackTraceString", BindingFlags.Instance | BindingFlags.NonPublic);
    remoteStackTraceString.SetValue(exception, exception.StackTrace);
    throw exception;
}

This puts the original stack trace in the _remoteStackTraceString field of the exception, which gets concatenated to the newly reset stack trace when the exception is re-thrown.

This is really a horrible hack, but it does achieve what you want. You are tinkering inside the System.Exception class though so this method may therefore break in subsequent releases of the framework.

adrianbanks
  • 81,306
  • 22
  • 176
  • 206
  • 2
    this is awesome, for when you have a *genuine* need to rethrow an exception in a different place but keep the old stack trace (like in a unit testing framework) – Rob Fonseca-Ensor Nov 24 '09 at 13:34
2

Although you may feel that the TargetInvocationException is "useless", it's the reality. Don't try to pretend that .NET didn't take the original exception and wrap it with a TargetInvocationException and throw it. That really happened. Some day, you might even want some piece of information that comes from that wrapping - like maybe the location of the code that threw the TargetInvocationException.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
0

You can't do that. throw always resets the stack trace, unless used without parameter. I'm afraid your callers will have to use the InnerException...

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
0

Using the "throw" keyword with an exception will always reset the stack trace.

The best thing to do is to catch the actual exception you want, and use "throw;" instead of "throw ex;". Or to throw your own exception, with the InnerException that you want to pass along.

I don't believe what you want to do is possible.

Nate
  • 30,286
  • 23
  • 113
  • 184
0

As others have said, use the "throw" keyword without adding to it to keep the exception chain intact. If you need that original exception (assuming that is what you mean) then you could call Exception.GetBaseException() at the end of your chain to get the Exception that started it all.

Zensar
  • 817
  • 4
  • 8
0

It is possible with .net 4.5:

catch(Exception e)
{
   ExceptionDispatchInfo.Capture(e.InnerException).Throw();
}
Eric Bole-Feysot
  • 13,949
  • 7
  • 47
  • 53