C Async Exception Wrapping
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
In C#, exceptions thrown inside async methods are captured and wrapped in the returned Task rather than thrown immediately. When you await that task, the runtime unwraps the exception and rethrows the original exception. However, if you access Task.Result or Task.Wait() instead, the exception is wrapped in an AggregateException. Understanding this wrapping behavior is essential for writing correct error handling in async code.
How Async Exceptions Work
When DoWorkAsync() is called, the exception does not propagate immediately. Instead, it is stored in the returned Task object. How you observe the task determines what exception you see.
await Unwraps the Exception
await unwraps the first exception from the Task and rethrows it as-is. This is the recommended pattern — it behaves like synchronous exception handling.
Task.Wait() and Task.Result Wrap in AggregateException
Task.Wait() and Task.Result throw AggregateException because a Task can represent multiple operations (e.g., Task.WhenAll), each of which might fail independently.
Multiple Exceptions with Task.WhenAll
Exception Filters
Use exception filters to catch specific async exceptions:
Exception filters (when) do not unwind the stack, preserving the original call stack for debugging.
Preserving Stack Traces
async void Exception Behavior
async void methods cannot be awaited, so their exceptions propagate to the SynchronizationContext and typically crash the application.
ConfigureAwait and Exceptions
Common Pitfalls
- Using
.Resultor.Wait(): These wrap exceptions inAggregateExceptionand can cause deadlocks in UI or ASP.NET contexts. Always useawaitinstead. async voidmethods: Exceptions inasync voidcannot be caught by the caller. They crash the process unless caught inside the method. Only useasync voidfor event handlers.- Swallowing exceptions with
Task.WhenAll:await Task.WhenAll(...)only rethrows the first exception. Capture the task and inspecttask.Exception.InnerExceptionsto see all failures. throw exinstead ofthrow:throw exresets the stack trace, making it impossible to find where the exception originally occurred. Always use barethrowto rethrow.- Catching
AggregateExceptionwhen usingawait: Withawait, you never seeAggregateException— the original exception is unwrapped. Only catchAggregateExceptionwhen using.Wait()or.Result.
Summary
awaitunwraps exceptions from tasks and rethrows the original — use this patternTask.Wait()andTask.Resultwrap exceptions inAggregateException— avoid theseTask.WhenAllcaptures multiple exceptions butawaitonly rethrows the firstasync voidmethods cannot propagate exceptions to callers — only use for event handlers- Use
throw(notthrow ex) to preserve stack traces when rethrowing - Use
ExceptionDispatchInfo.Capture()when you need to store and rethrow later
Related reading
- C async method that also has anonymous callback handlers does not flow correctly
- C Async Socket Client Blocks Main Interface Thread
- C Async Socket Server - BeginReceive in Callback
- C async vs OpenMP tasks
- C async/await strange behavior in console app
- C asynchronous delegates, thread scheduling
- C can't cast bool to int
- C equivalent to Java's Exception.printStackTrace?

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.