Here is a good explanation in details, by Stephen Toub, why there is a difference in exception type between Task.Wait() and await:
Task Exception Handling in .NET 4.5
When designing Task.Wait in .NET 4, we chose always propagating an
aggregate. That decision was influenced by the need to not overwrite
details, but also by the primary use case for tasks at the time, that
of fork/join parallelism, where the potential for multiple exceptions
is quite common.
While similar to Task.Wait at a high level (i.e. forward progress
isn’t made until the task completes), “await task” represents a very
different primary set of scenarios. Rather than being used for
fork/join parallelism, the most common usage of “await task” is in
taking a sequential, synchronous piece of code and turning it into a
sequential, asynchronous piece of code. In places in your code where
you perform a synchronous operation, you replace it with an
asynchronous operation represented by a task and “await” it. As such,
while you can certainly use await for fork/join operations (e.g.
utilizing Task.WhenAll), it’s not the 80% case. Further, .NET 4.5
sees the introduction of
System.Runtime.ExceptionServices.ExceptionDispatchInfo, which solves
the problem of allowing you to marshal exceptions across threads
without losing exception details like stack trace and Watson buckets.
Given an exception object, you pass it to
ExceptionDispatchInfo.Create, which returns an ExceptionDispatchInfo
object that contains a reference to the Exception object and a copy of
the its details. When it’s time to throw the exception, the
ExceptionDispatchInfo’s Throw method is used to restore the contents
of the exception and throw it without losing the original information
(the current call stack information is appended to what’s already
stored in the Exception).
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. 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 an AggregateException that contains all of the exceptions, so
you can catch whichever is thrown and go back to consult
Task.Exception when needed. Yes, this leads to a discrepancy between
exception behavior when switching between “task.Wait()” and “await
task”, but we’ve viewed that as the significant lesser of two evils.
await
– Codfish