97

When C# throws an exception, it can have an inner exception. What I want to do is get the inner-most exception, or in other words, the leaf exception that doesn't have an inner exception. I can do this in a while loop:

while (e.InnerException != null)
{
    e = e.InnerException;
}

But I was wondering if there was some one-liner I could use to do this instead.

DavidRR
  • 18,291
  • 25
  • 109
  • 191
Daniel T.
  • 37,212
  • 36
  • 139
  • 206
  • 4
    I'm throwing an exception in one of my classes, but my class is being used by a library that swallows all exceptions and throws its own. Problem is, the library exception is very generic and I need to know which specific exception I threw to know how to handle the problem. To make things worse, the library will throw its own exception multiple times nested in each other, to an arbitrary depth. So for example, it will throw `LibraryException -> LibraryException -> LibraryException -> MyException`. My exception is always the last on the chain and does not have its own inner exception. – Daniel T. Oct 06 '10 at 20:27
  • Oh, I get why you might step down to the innermost, having done so myself (and variants, such as innermost that derives from a particular type) but I don't get what the issue is with what you already have. – Jon Hanna Oct 06 '10 at 20:57
  • Oh, I see what you mean. I was just wondering if there was another way of writing it. I've gotten so used to using `LINQ` to do nearly everything that it seems weird when I have to write a loop. I work mostly with data access so I pretty much work with `ICollections` for nearly everything I do. – Daniel T. Oct 06 '10 at 21:16
  • @Daniel, isn't `LINQ` just masking the fact that the code still essentially has to loop? Are there any true set-based commands in .NET? – Brad Oct 07 '10 at 13:00
  • @Brad: Yes, `LINQ` of course uses loops under the covers, but I don't have to write that part myself. I'd much rather write `list.ForEach(Console.WriteLine)` than `foreach (var item in list) { Console.WriteLine(item); }`. – Daniel T. Oct 07 '10 at 19:29
  • 6
    The most correct answer is lurking near the bottom with (currently) only 2 votes -- Exception.GetBaseException(). This has been in the framework basically forever. Thanks to batCattle for the sanity-check. – Jay Nov 10 '11 at 15:50
  • http://stackoverflow.com/a/1456603/1135871 may also be useful. – Vahid Hassani Aug 01 '12 at 10:52
  • Maybe ***useful*** https://gist.github.com/benbrandt22/8676438 – Kiquenet Dec 03 '15 at 14:09
  • Possible duplicate of [Proper way to handle InnerException trees?](http://stackoverflow.com/questions/2085196/proper-way-to-handle-innerexception-trees) – Vinko Vrsalovic Dec 08 '15 at 11:27

12 Answers12

157

Oneliner :)

while (e.InnerException != null) e = e.InnerException;

Obviously, you can't make it any simpler.

As said in this answer by Glenn McElhoe, it's the only reliable way.

Community
  • 1
  • 1
Draco Ater
  • 20,820
  • 8
  • 62
  • 86
  • 9
    GetBaseException() method does this w/o a loop. http://msdn.microsoft.com/en-us/library/system.exception.getbaseexception.aspx – codingoutloud May 14 '13 at 21:22
  • 8
    This works whereas `Exception.GetBaseException()` didn't for me. – Tarik May 16 '13 at 21:20
  • Wouldent this code change the "e" source exception? and Shouldnt we make a copy of "e" before assigning InnerException to it? I suggest: var copyOfException = e; while (copyOfException.InnerException != null) copyOfException = copyOfException.InnerException; – Mohammad Reza Sadreddini Jul 29 '21 at 08:33
128

I believe Exception.GetBaseException() does the same thing as these solutions.

Caveat: From various comments we've figured out it doesn't always literally do the same thing, and in some cases the recursive/iterating solution will get you further. It is usually the innermost exception, which is disappointingly inconsistent, thanks to certain types of Exceptions that override the default. However if you catch specific types of exceptions and make reasonably sure they're not oddballs (like AggregateException) then I would expect it gets the legitimate innermost/earliest exception.

James Coyle
  • 9,922
  • 1
  • 40
  • 48
Josh Sutterfield
  • 1,943
  • 2
  • 16
  • 25
  • 3
    +1 There is nothing like knowing the BCL to avoid convoluted solutions. Clearly, this is the most correct answer. – Jay Nov 10 '11 at 15:48
  • 4
    For some reason, `GetBaseException()` didn't return the very first root exception. – Tarik May 16 '13 at 20:50
  • 4
    Glenn McElhoe pointed out that indeed GetBaseException() does not always do what MSDN suggests we can expect in general (that "the Exception that is the root cause of one or more subsequent exceptions"). In AggregateException it is limited to the lowest exception of the same type, and perhaps there are others. – Josh Sutterfield Jan 08 '14 at 21:26
  • 1
    Doesn't work for my issue. I have a few more levels down than what this is providing. – Giovanni B Mar 17 '15 at 19:02
  • 1
    I used this `ex.GetBaseException().GetBaseException().GetBaseException()` to get to the deeptest exception. – Michael R May 01 '15 at 22:40
  • 2
    `GetBaseException()` did not work for me, because the top-most exception is an AggregateException. – Chris Ballance Oct 07 '15 at 16:48
  • Honestly with AggregateException there is no base exception per se, because there are multiple exceptions, each having a base exception. You would almost need a new AggregateException strictly reflecting the base exceptions of each exception in the original AggregateException. But that's not a real thrown exception. For AggregateException the ideal call would be plural, i.e. `Exception[] GetBaseExceptions()` but that's not a thing. – Josh Sutterfield Sep 21 '21 at 16:28
25

Looping through InnerExceptions is the only reliable way.

If the caught exception is an AggregateException, then GetBaseException() returns only the innermost AggregateException.

http://msdn.microsoft.com/en-us/library/system.aggregateexception.getbaseexception.aspx

Glenn McElhoe
  • 301
  • 4
  • 2
  • 1
    Good catch. Thanks - that explains the comments above where that answer was not working for some people. This makes it clear that MSDN's general description of "root cause of one or more subsequent exceptions" is not always what you'd assume since derived classes may override it. The real rule seems to be that all exceptions in a chain of exceptions "agree" on GetBaseException() & return the same object, which seems to be the case for AggregateException. – Josh Sutterfield Jan 08 '14 at 21:44
  • What about `e.GetBaseException().GetBaseException();`? as mentioned [in this answer](https://stackoverflow.com/a/58306177/4108016) – Muhammad Sulaiman Aug 05 '22 at 08:41
13

If you don't know how deep the inner exceptions are nested, there is no way around a loop or recursion.

Of course, you can define an extension method that abstracts this away:

public static class ExceptionExtensions
{
    public static Exception GetInnermostException(this Exception e)
    {
        if (e == null)
        {
            throw new ArgumentNullException("e");
        }

        while (e.InnerException != null)
        {
            e = e.InnerException;
        }

        return e;
    }
}
dtb
  • 213,145
  • 36
  • 401
  • 431
10

I know this is an old post, but I'm surprised nobody suggested GetBaseException() which is a method on the Exception class:

catch (Exception x)
{
    var baseException = x.GetBaseException();
}

This has been around since .NET 1.1. Documentation here: http://msdn.microsoft.com/en-us/library/system.exception.getbaseexception(v=vs.71).aspx

Matt
  • 6,787
  • 11
  • 65
  • 112
2

You could use recursion to create a method in a utility class somewhere.

public Exception GetFirstException(Exception ex)
{
    if(ex.InnerException == null) { return ex; } // end case
    else { return GetFirstException(ex.InnerException); } // recurse
}

Use:

try
{
    // some code here
}
catch (Exception ex)
{
    Exception baseException = GetFirstException(ex);
}

The extension method suggested (good idea @dtb)

public static Exception GetFirstException(this Exception ex)
{
    if(ex.InnerException == null) { return ex; } // end case
    else { return GetFirstException(ex.InnerException); } // recurse
}

Use:

try
{
    // some code here
}
catch (Exception ex)
{
    Exception baseException = ex.GetFirstException();
}
Kiquenet
  • 14,494
  • 35
  • 148
  • 243
Brad
  • 15,361
  • 6
  • 36
  • 57
  • **Useful** `public static Exception GetOriginalException(this Exception ex) { if (ex.InnerException == null) return ex; return ex.InnerException.GetOriginalException(); }` – Kiquenet Dec 03 '15 at 14:34
  • Not applies `AggregateException.InnerExceptions` ? – Kiquenet Apr 09 '18 at 11:25
2

Sometimes you might have many inner exceptions (many bubbled exceptions). In which case you might want to do:

List<Exception> es = new List<Exception>();
while(e.InnerException != null)
{
   es.add(e.InnerException);
   e = e.InnerException
}
Ryan Ternier
  • 8,714
  • 4
  • 46
  • 69
1

In fact is so simple, you could use Exception.GetBaseException()

Try
      //Your code
Catch ex As Exception
      MessageBox.Show(ex.GetBaseException().Message, My.Settings.MsgBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
End Try
armadillo.mx
  • 934
  • 1
  • 11
  • 17
1

Not quite one line but close:

        Func<Exception, Exception> last = null;
        last = e => e.InnerException == null ? e : last(e.InnerException);
Steve Ellinger
  • 3,957
  • 21
  • 19
1

You have to loop, and having to loop, it's cleaner to move the loop into a separate function.

I created an extension method to deal with this. It returns a list of all of the inner exceptions of the specified type, chasing down Exception.InnerException and AggregateException.InnerExceptions.

In my particular problem, chasing down the inner exceptions was more complicated than usual, because the exceptions were being thrown by the constructors of classes that were being invoked through reflection. The exception we were catching had an InnerException of type TargetInvocationException, and the exceptions we actually needed to look at were buried deep in the tree.

public static class ExceptionExtensions
{
    public static IEnumerable<T> innerExceptions<T>(this Exception ex)
        where T : Exception
    {
        var rVal = new List<T>();

        Action<Exception> lambda = null;
        lambda = (x) =>
        {
            var xt = x as T;
            if (xt != null)
                rVal.Add(xt);

            if (x.InnerException != null)
                lambda(x.InnerException);

            var ax = x as AggregateException;
            if (ax != null)
            {
                foreach (var aix in ax.InnerExceptions)
                    lambda(aix);
            }
        };

        lambda(ex);

        return rVal;
    }
}

Usage is pretty simple. If, for example, you want to know if we encountered a

catch (Exception ex)
{
    var myExes = ex.innerExceptions<MyException>();
    if (myExes.Any(x => x.Message.StartsWith("Encountered my specific error")))
    {
        // ...
    }
}
Jeff Dege
  • 11,190
  • 22
  • 96
  • 165
1

I ran into this and wanted to be able to list all of the exception messages from the exception "stack". So, I came up with this.

public static string GetExceptionMessages(Exception ex)
{
    if (ex.InnerException is null)
        return ex.Message;
    else return $"{ex.Message}\n{GetExceptionMessages(ex.InnerException)}";
}
Ken Lamb
  • 59
  • 1
  • 6
0

Another way you could do it is by calling GetBaseException() twice:

Exception innermostException = e.GetBaseException().GetBaseException();

This works because if it is an AggregateException, the first call gets you to the innermost non-AggregateException then the second call gets you to the innermost exception of that exception. If the first exception is not an AggregateException, then the second call just returns the same exception.

  • `AggregateException` has no proper innermost exception because there are multiple "root cause" exceptions (typically from separate tasks awaited in aggregate). `AggregateException.GetBaseException()` yields _itself_, not an underlying exception. In fact its own `InnerException` is just `AggregateException.InnerExceptions.First()`. The best you could do is something like: `AggregateException.InnerExceptions.SelectMany(innerEx => innerEx.GetBaseException())` to get each base exception. They're all on equal footing. – Josh Sutterfield Dec 20 '22 at 18:53