### Review
- If the purpose is to output each of the descendant inner exceptions, you are missing all except the first inner exception in case of an `AggregateException`. And this is a common exception.
- Don't use `\n` as new line unless you specifically need to comply to this style of new line. Prefer `Environment.NewLine`.
- I am not convinced of the `exception != null` -> `string.Empty` clause. This seems to be coded just so you can make a one-liner method implementation. I would check for `exception.InnerException == null` to short-circuit the return value.
- `msgCount` is not hierarchical, so it does not make much sense when dealing with exception trees (see `AggregateException`). Consider using a count tree ("1.1.4", etc..) or not using a count at all. Perhaps an indentation suits the layout better.
- Does `exception.ToString()` provide sufficient information for you? It handles inner exceptions and inner exception trees.
### Alternative
This is your code reworked to take into account all the above.
public static string GetExceptionMessages(Exception exception, string indent = "\t")
{
exception = exception ?? throw new ArgumentNullException(nameof(exception));
indent = indent ?? string.Empty;
var builder = new StringBuilder();
GetExceptionMessages(exception, builder, indent, new Stack<string>());
return builder.ToString();
}
private static void GetExceptionMessages(
Exception exception, StringBuilder builder, string indent, Stack<string> currentIndent)
{
if (exception == null) return;
builder.AppendLine($"{string.Join(string.Empty, currentIndent)}{exception.Message}");
currentIndent.Push(indent);
if (exception is AggregateException aggregateException)
{
foreach (var innerException in aggregateException.InnerExceptions)
{
GetExceptionMessages(innerException, builder, indent, currentIndent);
}
}
else
{
GetExceptionMessages(exception.InnerException, builder, indent, currentIndent);
}
currentIndent.Pop();
}