I\'m running into a few issues where I call flatten
on an AggregateException
, but inside there is still ANOTHER AggregateException
! T
Normally AggregateException is used to consolidate multiple failures into a single, throwable exception object.
try {
Task.WaitAll(tasks)
}
catch (AggregateException ae) {
ae.Handle((x) =>
{
if (x is UnauthorizedAccessException) // This we know how to handle.
{
//do your code here
}
return true; //if you do something like this all exceptions are marked as handled
});
}
This is an old question, but the problem experienced by the OP is that await does not expose the AggregateException from the awaited task, but instead just the first exception in the AggregateException. So the catch(AggregateException ex) block is bypassed and the exception is caught further up the stack. So the code should have been 'simply':
retryNeeded = false;
do
{
try
{
if (retryNeeded)
await Task.Delay(500); // substituted for Thread.Sleep
using (var stream = file.Open(FileMode.Append, FileAccess.Write, FileShare.None))
{
using (StreamWriter writer = new StreamWriter(stream))
{
await writer.WriteLineAsync(data);
retryNeeded = false;
}
}
}
catch (IOException)
{
retryNeeded = true;
retryLeft--;
}
catch (Exception ex)
{
logger.ErrorException("Could not write to exception file: " + data, ex);
throw;
}
} while (retryNeeded && retryLeft > 0);
return (retryLeft > 0);
Alternatively, Jon Skeet's WithAllExceptions extension method allows one to 'protect' the AggregateException from await's behavior by wrapping the task in another task so you get an AggregateException containing an AggregateException and await 'returns' the original/inner AggregateException.
NOTE: AggregateException.Flatten really does 'flatten' recursively, as is shown by the example on the MSDN page.
EDIT: Improved delay on retryNeeded to avoid setting a bad async example.
Keep in mind that the 'flatten' method will give you a list of Exceptions, but can still leave you with a flattened InnerExceptions inside of each Exception.
So I found that this was not really sufficient:
try
{
// something dangerous
}
catch (AggregateException ae)
{
foreach(Exception innerException in ae.Flatten().InnerExceptions)
{
Console.WriteLine(innerException.Message());
}
}
Because this exception:
System.Net.Http.HttpRequestException: An error occurred while sending the request. ---> System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 192.168.42.55:443 at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult) at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Exception& exception) --- End of inner exception stack trace --- at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult, TransportContext& context) at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar) --- End of inner exception stack trace ---
Would end up like this:
An error occurred while sending the request.
The fix was something like this:
foreach(Exception exInnerException in aggEx.Flatten().InnerExceptions)
{
Exception exNestedInnerException = exInnerException;
do
{
if (!string.IsNullOrEmpty(exNestedInnerException.Message))
{
Console.WriteLine(exNestedInnerException.Message);
}
exNestedInnerException = exNestedInnerException.InnerException;
}
while (exNestedInnerException != null);
}
Resulting in:
An error occurred while sending the request.
Unable to connect to the remote server
A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 192.168.42.54:443
Hope that helps someone.
Try the sample code below, this should avoid await
unwrapping the AggregateException
and throw the original AggregateException at the point where task.result is called.
var task = writer.WriteLineAsync(data);
await task.ContinueWith(t => { }, TaskContinuationOptions.ExecuteSynchronously));
return task.Result;
Yes, there's exactly what you're asking for:
AggreggateException.Flatten()
will go through and compress everything down to a single AggregateException. So you can use it to loop through the all the inner exceptions like this:
try
{
// something dangerous
}
catch (AggregateException ae)
{
foreach(var innerException in ae.Flatten().InnerExceptions)
{
// handle error
}
}
MSDN link: http://msdn.microsoft.com/en-us/library/system.aggregateexception.flatten.aspx