I want await to throw AggregateException, not just the first Exception
I disagree with the implication in your question title that await
's behavior is undesired. It makes sense in the vast majority of scenarios. In a WhenAll
situation, how often do you really need to know all of the error details, as opposed to just one?
The main difficulty with AggregateException
is the exception handling, i.e., you lose the ability to catch a particular type.
That said, you can get the behavior you want with an extension method:
public static async Task WithAggregateException(this Task source)
{
try
{
await source.ConfigureAwait(false);
}
catch
{
// source.Exception may be null if the task was canceled.
if (source.Exception == null)
throw;
// EDI preserves the original exception's stack trace, if any.
ExceptionDispatchInfo.Capture(source.Exception).Throw();
}
}
AggregateException from Task.WhenAll only contains first exception when awaited
What you are observing is the behavior of the await
operator, not the behavior of the Task.WhenAll
method. If you are interested in why the await
behaves this way, you could read this article from the early days of async/await:
Having the choice of always throwing the first or always throwing an aggregate, for
await
we opt to always throw the first. This doesn’t mean, though, that you don’t have access to the same details. In all cases, the Task’s Exception property still returns anAggregateException
that contains all of the exceptions, so you can catch whichever is thrown and go back to consultTask.Exception
when needed. Yes, this leads to a discrepancy between exception behavior when switching betweentask.Wait()
andawait task
, but we’ve viewed that as the significant lesser of two evils.
In case you would like to implement a method similar in behavior to Task.WhenAll
,
but without losing the convenience of the async/await machinery, it is tricky but there are workarounds available here.
How to return AggregateException from async method
async
methods are designed to only every set at most a single exception on the returned task, not multiple.
This leaves you with two options, you can either not use an async
method to start with, instead relying on other means of performing your method:
public Task MyWhenAll(Task t1, Task t2)
{
return Task.Delay(TimeSpan.FromMilliseconds(100))
.ContinueWith(_ => Task.WhenAll(t1, t2))
.Unwrap();
}
If you have a more complex method that would be harder to write without using await
, then you'll need to unwrap the nested aggregate exceptions, which is tedious, although not overly complex, to do:
public static Task UnwrapAggregateException(this Task taskToUnwrap)
{
var tcs = new TaskCompletionSource<bool>();
taskToUnwrap.ContinueWith(task =>
{
if (task.IsCanceled)
tcs.SetCanceled();
else if (task.IsFaulted)
{
if (task.Exception is AggregateException aggregateException)
tcs.SetException(Flatten(aggregateException));
else
tcs.SetException(task.Exception);
}
else //successful
tcs.SetResult(true);
});
IEnumerable<Exception> Flatten(AggregateException exception)
{
var stack = new Stack<AggregateException>();
stack.Push(exception);
while (stack.Any())
{
var next = stack.Pop();
foreach (Exception inner in next.InnerExceptions)
{
if (inner is AggregateException innerAggregate)
stack.Push(innerAggregate);
else
yield return inner;
}
}
}
return tcs.Task;
}
Why doesn't await on Task.WhenAll throw an AggregateException?
I don't exactly remember where, but I read somewhere that with new async/await keywords, they unwrap the AggregateException
into the actual exception.
So, in catch block, you get the actual exception and not the aggregated one. This helps us write more natural and intuitive code.
This was also needed for easier conversion of existing code into using async/await where the a lot of code expects specific exceptions and not aggregated exceptions.
-- Edit --
Got it:
An Async Primer by Bill Wagner
Bill Wagner said: (in When Exceptions Happen)
...When you use await, the code generated by the compiler unwraps the
AggregateException and throws the underlying exception. By leveraging
await, you avoid the extra work to handle the AggregateException type
used by Task.Result, Task.Wait, and other Wait methods defined in the
Task class. That’s another reason to use await instead of the
underlying Task methods....
Wait for async Task without wrapping exceptions in AggregateException
I am going to use these in a command line application. So I need to call them synchronously a lot.
No, you don't. You can use async
-await
in a console application, you just need to make an async to sync transition at the very top. And you can do that by using Wait()
:
public static void Main()
{
MainAsync().Wait();
}
public static async Task MainAsync()
{
var datastore = …;
await datastore.SaveAsync();
}
Usually, combining await
with Wait()
is a bad idea (it can cause deadlocks), but it's the right solution here.
Note that if SaveAsync()
throws an exception and you don't catch it, it will be rethrown as AggregateException
from the Wait()
. But you can catch it as the original exception in MainAsync()
(because it doesn't use Wait()
).
If you really wanted to get the first exception thrown directly, you could do something similar to what await
does: task.GetAwaiter().GetResult()
. Note that if the Task
contains more than one exception, you will get only the first one (but the same applies to await
).
Since C# 7.1, you can make your Main
method async
and the compiler will write the transition code for you:
public static async Task Main()
{
var datastore = …;
await datastore.SaveAsync();
}
When I use a method returning
Task<TResult>
,task.Result
throwsAggregateException
even though there are no continuation tasks set. Why is this happening?
This has nothing to do with continuations. A single Task
can represent multiple operations, and each of them can throw an exception. Because of that, Task
methods always throw the exceptions wrapped in an AggregateException
.
I also have tried
task.RunSynchronously()
That doesn't make any sense. RunSynchronously()
can only be used on Task
s that were created using the Task
constructor. That's not the case here, so you can't use it. Task
s returned from async methods are always already started.
Will awaiting multiple tasks observe more than the first exception?
Task.WhenAll
returns a task and like all tasks the Exception
property holds an AggregateException
that combines all exceptions.
When you await
such a task only the first exception will actually be thrown.
... Whether because of child tasks that fault, or because of combinators like Task.WhenAlll, a single task may represent multiple operations, and more than one of those may fault. In such a case, and with the goal of not losing exception information (which can be important for post-mortem debugging), we want to be able to represent multiple exceptions, and thus for the wrapper type we chose AggregateException.
... Given that, and again having the choice of always throwing the first or always throwing an aggregate, for “await” we opt to always throw the first
from Task Exception Handling in .NET 4.5
It's up to you to choose if you want to handle just the first using await task;
(true in most cases) or handle all using task.Exception
(as in my example below), but in both cases a
and b
would not raise an UnobservedTaskException
.
var task = Task.WhenAll(a, b);
try
{
await task;
}
catch
{
Trace.WriteLine(string.Join(", ", task.Exception.Flatten().InnerExceptions.Select(e => e.Message)));
}
How to throw/catch the inner exception from a task?
When exception is thrown in task then using Result
property you will get AggregateException
where the real exception is in InnerException
property of this AggregateException
object (your exception is wrapped by AggregateException
).
To get the true exception (unwrapped exception) you can use GetAwaiter().GetResult()
:
var result = taskThatThrowsException.GetAwaiter().GetResult();
You can also use Result
property but then you should specify some condition for exception handling;
try
{
var result = taskThatThrowsException.Result;
}
catch (AggregateException ex) when (ex.InnerException is MyException myException)
{
// use myException - it is your true unwrapped exception
}
But you should NOT block asynchronous code - you should asynchronously wait - use await
and you will get your true unwrapped exception also:
var result = await taskThatThrowsException;
Related Topics
How to Catch Exception in Task
Read SQL Table into C# Datatable
Searching for a Specific Jtoken by Name in a Jobject Hierarchy
Embedding Unmanaged Dll into a Managed C# Dll
Headless Browser for C# (.Net)
Implementing Idisposable Correctly
Shouldserialize*() VS *Specified Conditional Serialization Pattern
C# Deserializing a Struct After Receiving It Through Tcp
How to Get Current User in ASP.NET Core
Do You Have to Put Task.Run in a Method to Make It Async
Detect Target Framework Version at Compile Time
In C#, How to Check If a Tcp Port Is Available
Web Reference VS. Service Reference
Raise an Event Whenever a Property's Value Changed