By TimothyP


2011-03-21 20:34:38 8 Comments

Using the async CTP from Microsoft for .NET, is it possible to catch an exception thrown by an async method in the calling method?

public async void Foo()
{
    var x = await DoSomethingAsync();

    /* Handle the result, but sometimes an exception might be thrown.
       For example, DoSomethingAsync gets data from the network
       and the data is invalid... a ProtocolException might be thrown. */
}

public void DoFoo()
{
    try
    {
        Foo();
    }
    catch (ProtocolException ex)
    {
          /* The exception will never be caught.
             Instead when in debug mode, VS2010 will warn and continue.
             The deployed the app will simply crash. */
    }
}

So basically I want the exception from the async code to bubble up into my calling code if that is even possible at all.

6 comments

@Stuart 2011-03-21 20:42:21

It's somewhat weird to read but yes, the exception will bubble up to the calling code - but only if you await or Wait() the call to Foo.

public async Task Foo()
{
    var x = await DoSomethingAsync();
}

public async void DoFoo()
{
    try
    {
        await Foo();
    }
    catch (ProtocolException ex)
    {
          // The exception will be caught because you've awaited
          // the call in an async method.
    }
}

//or//

public void DoFoo()
{
    try
    {
        Foo().Wait();
    }
    catch (ProtocolException ex)
    {
          /* The exception will be caught because you've
             waited for the completion of the call. */
    }
} 

Async void methods have different error-handling semantics. When an exception is thrown out of an async Task or async Task method, that exception is captured and placed on the Task object. With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started. - https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Note that using Wait() may cause your application to block, if .Net decides to execute your method synchronously.

This explanation http://www.interact-sw.co.uk/iangblog/2010/11/01/csharp5-async-exceptions is pretty good - it discusses the steps the compiler takes to achieve this magic.

@TimothyP 2011-03-21 20:46:57

Thnx for the quick reply, btw what do you mean it's weird, all suggestions are welcome, don't hold back , it's how I learn :p

@Stuart 2011-03-21 21:01:13

I actually mean it's straight-forward to read - whereas I know what's actually going on is really complicated - so my brain is telling me not to believe my eyes...

@Theo Yaung 2011-03-22 16:45:47

Exception propagation is one of my personal favorite parts of the design, in Async for C#/VB :-) Lambdas are nice for callbacks, but for chained async tasks, prior to C#/VB async there wasn't a clean way to unify exception handling. That's one of the really nice parts of baking it into the language.

@zmb 2013-11-03 14:47:39

Shouldn't DoFoo() be marked async here?

@MEMark 2013-12-13 14:08:52

@zmb, yes it shoulde be. The above code won't compile.

@mkamioner 2014-02-13 10:23:49

This was very helpful. The link you provided is dead though :-/

@Sornii 2014-03-26 01:50:43

I think the Foo() method should be marked as Task instead of void.

@Stuart 2014-03-26 07:58:06

@Sornii it's good practice to return Task - as then callers can choose to wait or await if they want to - but it's not essential.

@xanadont 2014-05-09 14:04:07

I'm pretty sure this will produce an AggregateException. As such, the catch block as appears in this answer will not catch the exception.

@Stuart 2014-05-12 10:34:04

@xanadont generally I don't think it does - see explanations like - stiller.co.il/blog/2012/12/task-wait-vs-await - plus I just tested with this quick gist in linqpad - gist.github.com/slodge/b88e68c96dedef494181

@xanadont 2014-05-12 16:03:45

Ah, you're right, @Stuart. If you remove the exception handling from Foo() and place it into Main(), then it'll get wrapped as an AggregateException.

@rism 2015-08-20 03:22:28

"but only if you await or Wait() the call to Foo" How can you await the call to Foo, when Foo is returning void? async void Foo(). Type void is not awaitable?

@Alexander Derck 2016-08-30 12:01:29

@xanadont Thanks, that was the thing I didn't grasp yet :)

@Hitesh P 2017-03-01 10:34:39

Cannot await void method, can it?

@Tselofan 2017-07-18 08:15:02

It's not possible await void method, or wait for it as Foo().Wait(); This wrong or incomplete answer.

@kudlatiger 2018-05-31 05:12:27

what if my caller is on other project and dll referenced? does exception reach here?

@Alois Kraus 2011-03-21 21:49:18

Your code doesn't do what you might think it does. Async methods return immediately after the method begins waiting for the async result. It's insightful to use tracing in order to investigate how the code is actually behaving.

The code below does the following:

  • Create 4 tasks
  • Each task will asynchronously increment a number and return the incremented number
  • When the async result has arrived it is traced.

 

static TypeHashes _type = new TypeHashes(typeof(Program));        
private void Run()
{
    TracerConfig.Reset("debugoutput");

    using (Tracer t = new Tracer(_type, "Run"))
    {
        for (int i = 0; i < 4; i++)
        {
            DoSomeThingAsync(i);
        }
    }
    Application.Run();  // Start window message pump to prevent termination
}


private async void DoSomeThingAsync(int i)
{
    using (Tracer t = new Tracer(_type, "DoSomeThingAsync"))
    {
        t.Info("Hi in DoSomething {0}",i);
        try
        {
            int result = await Calculate(i);
            t.Info("Got async result: {0}", result);
        }
        catch (ArgumentException ex)
        {
            t.Error("Got argument exception: {0}", ex);
        }
    }
}

Task<int> Calculate(int i)
{
    var t = new Task<int>(() =>
    {
        using (Tracer t2 = new Tracer(_type, "Calculate"))
        {
            if( i % 2 == 0 )
                throw new ArgumentException(String.Format("Even argument {0}", i));
            return i++;
        }
    });
    t.Start();
    return t;
}

When you observe the traces

22:25:12.649  02172/02820 {          AsyncTest.Program.Run 
22:25:12.656  02172/02820 {          AsyncTest.Program.DoSomeThingAsync     
22:25:12.657  02172/02820 Information AsyncTest.Program.DoSomeThingAsync Hi in DoSomething 0    
22:25:12.658  02172/05220 {          AsyncTest.Program.Calculate    
22:25:12.659  02172/02820 {          AsyncTest.Program.DoSomeThingAsync     
22:25:12.659  02172/02820 Information AsyncTest.Program.DoSomeThingAsync Hi in DoSomething 1    
22:25:12.660  02172/02756 {          AsyncTest.Program.Calculate    
22:25:12.662  02172/02820 {          AsyncTest.Program.DoSomeThingAsync     
22:25:12.662  02172/02820 Information AsyncTest.Program.DoSomeThingAsync Hi in DoSomething 2    
22:25:12.662  02172/02820 {          AsyncTest.Program.DoSomeThingAsync     
22:25:12.662  02172/02820 Information AsyncTest.Program.DoSomeThingAsync Hi in DoSomething 3    
22:25:12.664  02172/02756          } AsyncTest.Program.Calculate Duration 4ms   
22:25:12.666  02172/02820          } AsyncTest.Program.Run Duration 17ms  ---- Run has completed. The async methods are now scheduled on different threads. 
22:25:12.667  02172/02756 Information AsyncTest.Program.DoSomeThingAsync Got async result: 1    
22:25:12.667  02172/02756          } AsyncTest.Program.DoSomeThingAsync Duration 8ms    
22:25:12.667  02172/02756 {          AsyncTest.Program.Calculate    
22:25:12.665  02172/05220 Exception   AsyncTest.Program.Calculate Exception thrown: System.ArgumentException: Even argument 0   
   at AsyncTest.Program.c__DisplayClassf.Calculateb__e() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 124   
   at System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj)    
   at System.Threading.Tasks.Task.InnerInvoke()     
   at System.Threading.Tasks.Task.Execute()     
22:25:12.668  02172/02756 Exception   AsyncTest.Program.Calculate Exception thrown: System.ArgumentException: Even argument 2   
   at AsyncTest.Program.c__DisplayClassf.Calculateb__e() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 124   
   at System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj)    
   at System.Threading.Tasks.Task.InnerInvoke()     
   at System.Threading.Tasks.Task.Execute()     
22:25:12.724  02172/05220          } AsyncTest.Program.Calculate Duration 66ms      
22:25:12.724  02172/02756          } AsyncTest.Program.Calculate Duration 57ms      
22:25:12.725  02172/05220 Error       AsyncTest.Program.DoSomeThingAsync Got argument exception: System.ArgumentException: Even argument 0  

Server stack trace:     
   at AsyncTest.Program.c__DisplayClassf.Calculateb__e() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 124   
   at System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj)    
   at System.Threading.Tasks.Task.InnerInvoke()     
   at System.Threading.Tasks.Task.Execute()     

Exception rethrown at [0]:      
   at System.Runtime.CompilerServices.TaskAwaiter.EndAwait()    
   at System.Runtime.CompilerServices.TaskAwaiter`1.EndAwait()  
   at AsyncTest.Program.DoSomeThingAsyncd__8.MoveNext() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 106    
22:25:12.725  02172/02756 Error       AsyncTest.Program.DoSomeThingAsync Got argument exception: System.ArgumentException: Even argument 2  

Server stack trace:     
   at AsyncTest.Program.c__DisplayClassf.Calculateb__e() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 124   
   at System.Threading.Tasks.Task`1.InvokeFuture(Object futureAsObj)    
   at System.Threading.Tasks.Task.InnerInvoke()     
   at System.Threading.Tasks.Task.Execute()     

Exception rethrown at [0]:      
   at System.Runtime.CompilerServices.TaskAwaiter.EndAwait()    
   at System.Runtime.CompilerServices.TaskAwaiter`1.EndAwait()  
   at AsyncTest.Program.DoSomeThingAsyncd__8.MoveNext() in C:\Source\AsyncTest\AsyncTest\Program.cs:line 0      
22:25:12.726  02172/05220          } AsyncTest.Program.DoSomeThingAsync Duration 70ms   
22:25:12.726  02172/02756          } AsyncTest.Program.DoSomeThingAsync Duration 64ms   
22:25:12.726  02172/05220 {          AsyncTest.Program.Calculate    
22:25:12.726  02172/05220          } AsyncTest.Program.Calculate Duration 0ms   
22:25:12.726  02172/05220 Information AsyncTest.Program.DoSomeThingAsync Got async result: 3    
22:25:12.726  02172/05220          } AsyncTest.Program.DoSomeThingAsync Duration 64ms   

You will notice that the Run method completes on thread 2820 while only one child thread has finished (2756). If you put a try/catch around your await method you can "catch" the exception in the usual way although your code is executed on another thread when the calculation task has finished and your contiuation is executed.

The calculation method traces the thrown exception automatically because I did use the ApiChange.Api.dll from the ApiChange tool. Tracing and Reflector helps a lot to understand what is going on. To get rid of threading you can create your own versions of GetAwaiter BeginAwait and EndAwait and wrap not a task but e.g. a Lazy and trace inside your own extension methods. Then you will get much better understanding what the compiler and what the TPL does.

Now you see that there is no way to get in a try/catch your exception back since there is no stack frame left for any exception to propagate from. Your code might be doing something totally different after you did initiate the async operations. It might call Thread.Sleep or even terminate. As long as there is one foreground thread left your application will happily continue to execute asynchronous tasks.


You can handle the exception inside the async method after your asynchronous operation did finish and call back into the UI thread. The recommended way to do this is with TaskScheduler.FromSynchronizationContext. That does only work if you have an UI thread and it is not very busy with other things.

@Stephan Ghequiere 2017-11-09 09:37:03

This blog explains your problem neatly Async Best Practices.

The gist of it being you shouldn't use void as return for an async method, unless it's an async event handler, this is bad practice because it doesn't allow exceptions to be caught ;-).

Best practice would be to change the return type to Task. Also, try to code async all the way trough, make every async method call and be called from async methods. Except for a Main method in a console, which can't be async (before C# 7.1).

You will run into deadlocks with GUI and ASP.NET applications if you ignore this best practice. The deadlock occurs because these applications runs on a context that allows only one thread and won't relinquish it to the async thread. This means the GUI waits synchronously for a return, while the async method waits for the context: deadlock.

This behaviour won't happen in a console application, because it runs on context with a thread pool. The async method will return on another thread which will be scheduled. This is why a test console app will work, but the same calls will deadlock in other applications...

@Adam 2017-11-29 06:10:43

"Except for a Main method in a console, which can't be async." Since C# 7.1, Main can now be an async method link

@rohanjansen 2014-07-04 12:17:12

Its also important to note that you will lose the chronological stack trace of the exception if you you have a void return type on an async method. I would recommend returning Task as follows. Going to make debugging a whole lot easier.

public async Task DoFoo()
    {
        try
        {
            return await Foo();
        }
        catch (ProtocolException ex)
        {
            /* Exception with chronological stack trace */     
        }
    }

@Matias Grioni 2019-02-22 01:33:30

This will cause an issue with not all paths returning a value, since if there is an exception no value is returned, while in the try there is. If you have no return statement, this code works however, since the Task is "implicitly" returned by using async / await.

@Rob Church 2013-04-22 23:25:20

The reason the exception is not caught is because the Foo() method has a void return type and so when await is called, it simply returns. As DoFoo() is not awaiting the completion of Foo, the exception handler cannot be used.

This opens up a simpler solution if you can change the method signatures - alter Foo() so that it returns type Task and then DoFoo() can await Foo(), as in this code:

public async Task Foo() {
    var x = await DoSomethingThatThrows();
}

public async void DoFoo() {
    try {
        await Foo();
    } catch (ProtocolException ex) {
        // This will catch exceptions from DoSomethingThatThrows
    }
}

@GGleGrand 2016-03-18 07:19:42

This can really sneak up on you and should be warned by the compiler.

@Sanjeevakumar Hiremath 2011-03-21 20:50:02

The exception can be caught in the async function.

public async void Foo()
{
    try
    {
        var x = await DoSomethingAsync();
        /* Handle the result, but sometimes an exception might be thrown
           For example, DoSomethingAsync get's data from the network
           and the data is invalid... a ProtocolException might be thrown */
    }
    catch (ProtocolException ex)
    {
          /* The exception will be caught here */
    }
}

public void DoFoo()
{
    Foo();
}

@TimothyP 2011-03-21 21:01:21

Hey, I know but I really need that information in DoFoo so I can display the information in the UI. In this case it's important for the UI to display the exception as it is not an end user tool but a tool to debug a communication protocol

@Sanjeevakumar Hiremath 2011-03-21 21:03:08

In that case, callbacks make a lot of sense.(good old async delegates)

@Eric J. 2011-03-21 22:16:01

@Tim: Include whatever information you need in the thrown exception?

Related Questions

Sponsored Content

5 Answered Questions

[SOLVED] Catch multiple exceptions in one line (except block)

9 Answered Questions

[SOLVED] How to safely call an async method in C# without await

8 Answered Questions

[SOLVED] Why can't I use the 'await' operator within the body of a lock statement?

  • 2011-09-30 15:23:00
  • Kevin
  • 87633 View
  • 317 Score
  • 8 Answer
  • Tags:   c# .net async-await

6 Answered Questions

[SOLVED] Globally catch exceptions in a WPF application?

26 Answered Questions

[SOLVED] Why not inherit from List<T>?

21 Answered Questions

[SOLVED] How and when to use ‘async’ and ‘await’

24 Answered Questions

[SOLVED] How would I run an async Task<T> method synchronously?

28 Answered Questions

[SOLVED] Catch multiple exceptions at once?

6 Answered Questions

[SOLVED] HttpClient.GetAsync(...) never returns when using await/async

8 Answered Questions

[SOLVED] About catching ANY exception

Sponsored Content