Why Can't I Catch an Exception from Async Code

Why can't I catch an exception from async code?

You have the "Just my code" Option turned on. With this on, it is considering the exception unhandled with respect to "just your code"--because other code is catching the exception and stuffing it inside of a Task, later to be rethrown at the await call and caught by your catch statement.

Without being attached in the debugger, your catch statement will be triggered, and it will run as you expect. Or you can just continue from within the debugger and it will run as expected.

The better thing to do is to just turn off "Just my code". IMO, it causes more confusion than it is worth.

Exception thrown in async method is not caught - why?

  1. As described for example in this blog post by Stephen Cleary - the state machine for async methods will capture exceptions from your code and place them on the returned task, i.e. method invocation will not throw, you will be able to catch exception if await the result.

  2. As for TaskScheduler.UnobservedTaskException - check out this answer and be sure to run code in Release mode.

Why I couldn't catch the exception in async function that has void return type?

Any time you have async void, you're basically breaking the ability to correctly signal completion and failure; the only way it can report failure is if the exception happens immediately and before any incomplete await - i.e. synchronously. In your case, the Task.Run guarantees that this is not synchronous, hence any knowledge of the outcome and failure: is lost.

Fundamentally, never write async void (unless you absolutely have to, for example in an event-handler). In addition to the problem above, it also has known complications with some SynchronizationContext implementations (in particular the legacy ASP.NET one), which means simply invoking an async void method is enough to crash your application (at least hypothetically; the sync-context caveat applies more to library authors than application authors, since library authors don't get to choose the application execution environment).

Remove the async void. If you want to return "nothing", then you should use async Task or async ValueTask as the signature:

static async Task MyMethodAsync() {
await TestThrowException();
}

(which could perhaps also be simplified to)

static Task MyMethodAsync()
=> TestThrowException();

and:

static async Task Main(string[] args) {
try {
await MyMethodAsync();
}
catch (Exception e) {
Console.WriteLine("Catch");
}
Console.ReadLine();
}

Why is try {} .. catch() not working with async/await function?

You need to await errorTest

const callFunction=async()=>{
try{
const result = await errorTest()
}catch(err){
console.log(err)
}
}
callFunction ()

Note that the await errorTest() function has to also be in an async function. That's why I put it inside callFunction ()

Another Option

const errorTest = async() => { 
try{
const result = await $.get("http://dataa.fixer.io/api/latest?access_key=9790286e305d82fbde77cc1948cf847c&format=1");

console.log(result)
}catch(err){
console.log(err)
}
}

Catching Exceptions in async methods when not called with await

exception was thrown before an await was done, that it would execute synchronously

Thought this is fairly true, but it doesn't mean you could catch the exception.

Because your code has async keyword, which turns the method into an async state machine i.e. encapsulated / wrapped by a special type. Any exception thrown from async state machine will get caught and re-thrown when the task is awaited (except for those async void ones) or they go unobserved, which can be caught in TaskScheduler.UnobservedTaskException event.

If you remove async keyword from the NonAwaitedMethod method, you can catch the exception.

A good way to observe this behavior is using this:

try
{
NonAwaitedMethod();

// You will still see this message in your console despite exception
// being thrown from the above method synchronously, because the method
// has been encapsulated into an async state machine by compiler.
Console.WriteLine("Method Called");
}
catch (Exception e)
{
Console.WriteLine("Exception Caught");
}

So your code is compiled similarly to this:

try
{
var stateMachine = new AsyncStateMachine(() =>
{
try
{
NonAwaitedMethod();
}
catch (Exception ex)
{
stateMachine.Exception = ex;
}
});

// This does not throw exception
stateMachine.Run();
}
catch (Exception e)
{
Console.WriteLine("Exception Caught");
}

why does swapping from Task to void return type cause the exception to get caught

If the method returns a Task, the exception is caught by the task.

If the method is void, then the exception gets re-thrown from an arbitrary thread pool thread. Any unhandled exception thrown from thread pool thread will cause the app to crash, so chances are the debugger (or maybe the JIT debugger) is watching this sort of exceptions.

If you want to fire and forget but properly handle the exception, you could use ContinueWith to create a continuation for the task:

NonAwaitedMethod()
.ContinueWith(task => task.Exception, TaskContinuationOptions.OnlyOnFaulted);

Note you have to visit task.Exception property to make the exception observed, otherwise, task scheduler still will receive UnobservedTaskException event.

Or if the exception needs to be caught and processed in Main, the correct way to do that is using async Main methods.

Surprising case where exception handling in async method does not catch the exception

I don't understand the inconsistency in the second row of the table

The right side case (no state machines at all) is trivial:

  • you call a method inside a try/catch block
  • the method throws an exception
  • your catch black catches it - nothing special

The left side is actually easy to understand, too:

  • you call a method inside a try/catch block
  • but this method is not what it seems, it has been converted to a state
    machine, so what it returns is a Task that represents the execution of the method's content as you implemented it(1)
  • so as long as you don't Wait or await that returned Task or try to access its Result property, the exception is not (re-)thrown inside your try block.

(1) I wish my English was better to find a better and more exact description. As Servy pointed out, in your example an already faulted Task is returned.

Exception in async methods is not caught

In your first example the exception is not caught because it does not occure before leaving the try/catch block. If you want to catch it there you need to wait/await it there exactly like you do in the second example.
If you do not await the returned task the method continues execution and leaves the try/catch block before the exception actually occures...

If you want to catch the exception "out of band" you can also register to TaskScheduler.UnobservedTaskException (this event is called if a task is throwing an exception which is nowhere caught) to get all uncaught exceptions or monitor the tasks Exception property. May also check out THIS answer.

Catch an exception thrown by an async void method

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. */
}
}

As Stephen Cleary wrote in Async/Await - Best Practices in Asynchronous Programming:

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.

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.



Related Topics



Leave a reply



Submit