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.
Exception handling of async void methods
Why is the exception not caught by the catch statement?
Because it's async void
.
Can somebody explain the internals of exception handling of async void methods?
The Task
in an async Task
method represents the execution of that method. So when an async Task
method raises an exception, that exception is used to fault the task.
void
is an unnatural return type for async
methods. Among other issues, there is nowhere to place the exception thrown by an async void
method. 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 executing.
Is there a way to catch exceptions for async void methods?
You could provide your own SynchronizationContext
.
But then, exceptions in event handlers will not be handled?
The "raise exceptions on the SynchronizationContext
" behavior is intended to imitate the way exceptions work for event handlers. E.g. for a GUI application, the exception raised by a GUI event (Button_Click
) will be forwarded to the same top-level application error handler (Application.DispatcherUnhandledException
) whether the GUI event handler (Button_Click
) is synchronous or asynchronous.
Exception thrown in async method is not caught - why?
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 ifawait
the result.As for
TaskScheduler.UnobservedTaskException
- check out this answer and be sure to run code inRelease
mode.
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 await
ed (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.
Why does Exception from async void crash the app but from async Task is swallowed
TL;DR
This is because async void
shouldn't be used! async void
is only there to make legacy code work (e.g. event handlers in WindowsForms and WPF).
Technical details
This is because of how the C# compiler generates code for the async
methods.
You should know that behind async
/await
there's a state machine (IAsyncStateMachine
implementation) generated by the compiler.
When you declare an async
method, a state machine struct
will be generated for it. For your ex()
method, this state machine code will look like:
void IAsyncStateMachine.MoveNext()
{
try
{
throw new Exception();
}
catch (Exception exception)
{
this.state = -2;
this.builder.SetException(exception);
}
}
Note that this.builder.SetException(exception);
statement. For a Task
-returning async
method, this will be an AsyncTaskMethodBuilder
object. For a void ex()
method, it will be an AsyncVoidMethodBuilder
.
The ex()
method body will be replaced by the compiler with something like this:
private static Task ex()
{
ExAsyncStateMachine exasm;
exasm.builder = AsyncTaskMethodBuilder.Create();
exasm.state = -1;
exasm.builder.Start<ExAsyncStateMachine>(ref exasm);
return exasm.builder.Task;
}
(and for the async void ex()
, there will be no last return
line)
The method builder's Start<T>
method will call the MoveNext
method of the state machine. The state machine's method catches the exception in its catch
block. This exception should normally be observed on the Task
object - the AsyncTaskMethodBuilder.SetException
method stores that exception object in the Task
instance. When we drop that Task
instance (no await
), we don't see the exception at all, but the exception itself isn't thrown anymore.
In the state machine for async void ex()
, there's an AsyncVoidMethodBuilder
instead. Its SetException
method looks different: since there's no Task
where to store the exception, it has to be thrown. It happens in a different way, however, not just a normal throw
:
AsyncMethodBuilderCore.ThrowAsync(exception, synchronizationContext);
The logic inside that AsyncMethodBuilderCore.ThrowAsync
helper decides:
- If there's a
SynchronizationContext
(e.g. we're on a UI thread of a WPF app), the exception will be posted on that context. - Otherwise, the exception will be queued on a
ThreadPool
thread.
In both cases, the exception won't be caught by a try-catch
block that might be set up around the ex()
call (unless you have a special SynchronizationContext
that can do this, see e.g. Stephen Cleary's AsyncContext
).
The reason is simple: when we post a throw
action or enqueue it, we then simply return from the ex()
method and thus leave the try-catch
block. Then, the posted/enqueued action is executed (either on the same or on a different thread).
Catching an exception in an async method
First of all, your exception is handled. I believe you are seeing the code stop execution and display the error because you have Break When Thrown
on for exceptions. Check your Exception Window (Debug -> Windows -> Exception Settings
).
When you use a return type of void
on an async
method, you lack the ability to get any sort of information back from the method - it's fire and forget. Except in specific situations, this is bad practice. Always have your async methods return a Task
or Task<T>
:
private static async Task TestAsyncException()
Now, your main method can listen to the task:
static void Main(string[] args)
{
TestAsyncException().Wait(); // or whatever you want to do with the task
Console.Read();
}
Normally, you could use await
to unwrap the task here, but that's not allowed in the application entry point.
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 does Exception from async void crash the app but from async Task is swallowed
TL;DR
This is because async void
shouldn't be used! async void
is only there to make legacy code work (e.g. event handlers in WindowsForms and WPF).
Technical details
This is because of how the C# compiler generates code for the async
methods.
You should know that behind async
/await
there's a state machine (IAsyncStateMachine
implementation) generated by the compiler.
When you declare an async
method, a state machine struct
will be generated for it. For your ex()
method, this state machine code will look like:
void IAsyncStateMachine.MoveNext()
{
try
{
throw new Exception();
}
catch (Exception exception)
{
this.state = -2;
this.builder.SetException(exception);
}
}
Note that this.builder.SetException(exception);
statement. For a Task
-returning async
method, this will be an AsyncTaskMethodBuilder
object. For a void ex()
method, it will be an AsyncVoidMethodBuilder
.
The ex()
method body will be replaced by the compiler with something like this:
private static Task ex()
{
ExAsyncStateMachine exasm;
exasm.builder = AsyncTaskMethodBuilder.Create();
exasm.state = -1;
exasm.builder.Start<ExAsyncStateMachine>(ref exasm);
return exasm.builder.Task;
}
(and for the async void ex()
, there will be no last return
line)
The method builder's Start<T>
method will call the MoveNext
method of the state machine. The state machine's method catches the exception in its catch
block. This exception should normally be observed on the Task
object - the AsyncTaskMethodBuilder.SetException
method stores that exception object in the Task
instance. When we drop that Task
instance (no await
), we don't see the exception at all, but the exception itself isn't thrown anymore.
In the state machine for async void ex()
, there's an AsyncVoidMethodBuilder
instead. Its SetException
method looks different: since there's no Task
where to store the exception, it has to be thrown. It happens in a different way, however, not just a normal throw
:
AsyncMethodBuilderCore.ThrowAsync(exception, synchronizationContext);
The logic inside that AsyncMethodBuilderCore.ThrowAsync
helper decides:
- If there's a
SynchronizationContext
(e.g. we're on a UI thread of a WPF app), the exception will be posted on that context. - Otherwise, the exception will be queued on a
ThreadPool
thread.
In both cases, the exception won't be caught by a try-catch
block that might be set up around the ex()
call (unless you have a special SynchronizationContext
that can do this, see e.g. Stephen Cleary's AsyncContext
).
The reason is simple: when we post a throw
action or enqueue it, we then simply return from the ex()
method and thus leave the try-catch
block. Then, the posted/enqueued action is executed (either on the same or on a different thread).
How to handle the exception thrown by the async method with observable?
What's happening in your code is a direct consequence of you using Observable.Create
and filling the observable with this code:
Enumerable.Range(1, 10).ToList().ForEach(x =>
{
observer.OnNext(x);
});
Observable.Create
uses the current thread to create the observable, so the Enumerable.Range(1, 10).ToList().ForEach
executes immediately on the current thread and the call to OnNext
executes the handler(x).Wait()
immediately.
You'll note, though, that the exception occurs in the delegate passed to the Subscribe
. Internally there is code like this:
catch (Exception exception)
{
if (!autoDetachObserver.Fail(exception))
{
throw;
}
return autoDetachObserver;
}
That catches the exception in the subscribe, cancels the subscription - hence the "Observable Dispose"
message - and then rethrows the exception and that's where your code catches it.
Now, if you wanted to do this properly in Rx, you'd avoid Observable.Create
. It's a tempting way to create observables, but it leads to trouble.
Instead do this:
public async Task Test()
{
Func<int, Task> handler = async (i) =>
{
// simulate the handler logic
await Task.Delay(TimeSpan.FromSeconds(1));
// throw the exception to test
throw new Exception($"{i}");
};
await
Observable
.Range(1, 10)
.SelectMany(i => Observable.FromAsync(() => handler(i)))
.LastOrDefaultAsync();
}
But, of course, we want to handle the exception. The simple way is like this:
public async Task Test()
{
Func<int, Task> handler = async (i) =>
{
// simulate the handler logic
await Task.Delay(TimeSpan.FromSeconds(1));
// throw the exception to test
throw new Exception($"{i}");
};
await
Observable
.Range(1, 10)
.SelectMany(i =>
Observable
.FromAsync(() => handler(i))
.Catch<Unit, Exception>(ex =>
{
Console.WriteLine($"The exception is catch:{ex.ToString()}");
return Observable.Empty<Unit>();
}))
.LastOrDefaultAsync();
}
That now outputs the 10 exception errors and completes normally.
Related Topics
How to Cast Object of Type 'System.Dbnull' to Type 'System.String'
How to Perform a Left Outer Join Using Linq Extension Methods
Deciding Between Httpclient and Webclient
Why Does the Ef 6 Tutorial Use Asynchronous Calls
How to Retrieve Id of Inserted Entity Using Entity Framework
Is Floating-Point Math Consistent in C#? Can It Be
Calling Stored Procedure With Return Value
Why Do We Need Boxing and Unboxing in C#
Calculate the Number of Business Days Between Two Dates
Transparent Control Over Picturebox
Check If a Class Is Derived from a Generic Class
Replace Line Breaks in a String C#
The Foreach Identifier and Closures
Ef Core Returns Null Relations Until Direct Access
Dynamically Replace the Contents of a C# Method