Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exception.GetBaseException() returning exception with not null InnerException

I've this exception of type System.AggregateException:

Message = "One or more errors occurred."
Source = null
StackTrace = null

Its InnerException is this System.Reflection.TargetInvocationException:

Message = "Exception has been thrown by the target of an invocation."
Source = "mscorlib"
StackTrace = 
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at Microsoft.AspNet.SignalR.Hubs.HubDispatcher.Incoming(IHubIncomingInvokerContext context)

Its InnerException is this exception created by me that extends from ApplicationException:

Message = "Some error writen by me at the hub"
Source = "Chat"
StackTrace = 
      at Chat.Hubs.ChatHub.SendMessage(String text, String clientConnId) in d:\...Chat\Chat\Hubs\ChatHub.cs:line 48

When I run this:

Exception excpetion = ex.GetBaseException();

Where ex is the System.AggregateException I get the System.Reflection.TargetInvocationExceptionat excpetion. How can this happen?

Scenario

I don't know how to reproduce this in a simple project. In my case I found this with a SignalR project. Some hub method throwing an exception an handle the errors with this at the global.asax:

GlobalHost.HubPipeline.AddModule(new MyHubPipelineModule());

And MyHubPipelineModule should be like this:

public class MyHubPipelineModule : HubPipelineModule
{
    protected override void OnIncomingError(Exception ex, IHubIncomingInvokerContext context)
    {
        Exception excpetion = ex.GetBaseException();
        context.Hub.Clients.Caller.ExceptionHandler(excpetion.Message);
    }
}

Note: This should be done with SignalR 1.0.1. In the 1.1.0 The exception is simpler (smaller chain) so it works well. Make sure you have this packages versions:

<package id="Microsoft.AspNet.SignalR" version="1.0.1" targetFramework="net40" />
<package id="Microsoft.AspNet.SignalR.Core" version="1.0.1" targetFramework="net40" />
<package id="Microsoft.AspNet.SignalR.JS" version="1.0.1" targetFramework="net40" />
<package id="Microsoft.AspNet.SignalR.Owin" version="1.0.1" targetFramework="net40" />
<package id="Microsoft.AspNet.SignalR.SystemWeb" version="1.0.1" targetFramework="net40" />
like image 655
Diego Avatar asked May 15 '13 13:05

Diego


2 Answers

According to MSDN GetBaseException should behave like

public Exception GetBaseException()
{ 
    Exception result = this; 
    while (result.InnerException != null) 
        result = result.InnerException; 
    return result; 
}

And states

For all exceptions in a chain of exceptions, the GetBaseException method must return the same object (the base exception).

Which begs the question, why is this method virtual? It would seem that any override could only match the implementation or violate the contract.

According to MSDN AggregateException.GetBaseException

Returns the AggregateException that is the root cause of this exception.

By the OP's statement (and inspection of the source), "Returns the AggregateException" can be violated because the result is not always an AggregateException.

Frankly, I find the whole statement to be a non-sequitur as I would consider the 'first' NON-AggregateException to be "the root cause of this exception" insofar as one can identify a singular ("the") root cause. Since "regular" exceptions just get wrapped in AggregateExceptions as one moves toward the root of a parallel processing 'fan out'.

My best interpretation is that AggregateException.GetBaseException is intended to "unwrap" pointlessly non-parallel nested AggregateExceptions until reaching a level where 'fan out' actually occurs.

The bug here (LATER FIXED) would appear to be what happens when there IS no 'fan out', i.e. an AggregateException with just one (non-Aggregate) InnerException(s). In that case it returns that (non-Aggregate) Exception. .... which has a different GetBaseException implementation/interpretation.

EDIT 2020: At some point in the intervening years AggregateException.GetBaseException's implementation was fixed to match the above interpretation. @Jan-Slodicka's answer indicates it was fixed at least in Mono as far back as 2016.

like image 137
SensorSmith Avatar answered Sep 25 '22 04:09

SensorSmith


It seemed that the problem wasn't linked with SignalR. It has to do with AggregateExceptions.

I was looking at the MSDN page of AggregateException.GetBaseException Method and I found this:

Returns the AggregateException that is the root cause of this exception

So I guess that the documentation of Exception.GetBaseException Method is valid only if the method is not overridden.

like image 20
Diego Avatar answered Sep 21 '22 04:09

Diego