Where to catch exception in async code? - c#

Task task = AsyncMethod();
// do other stuff
await task;
AsyncMethod() can throw exceptions. Do I put the try-catch around the method invocation, the await, or both?

To avoid the whole debate of where the exception handling should happen, I'll just slightly change your question to: where is it possible to catch exceptions thrown from the AsyncMethod method.
The answer is: where you await it.
Assuming your AsyncMethod method looks something like this:
private async Task AsyncMethod()
{
// some code...
throw new VerySpecificException();
}
... then you can catch the exception this way:
Task task = AsyncMethod();
// do other stuff
try
{
await task;
}
catch(VerySpecificException e) // nice, I can use the correct exception type here.
{
// do something with exception here.
}
Just test it, and you will see how the await keyword does all the work of unwrapping and throwing the exception from the returned Task in a way that it feels very natural to code the try-catch block.
Relevant documentation: try-catch.
Notice what the Exceptions in Async Methods section says:
To catch the exception, await the task in a try block, and catch the exception in the associated catch block.

Related

Exception filter for the class many different methods

In my client application that uses webApi I have a plethora of methods that make calls to webApi asynchronously like this:
var task = Task.Run(async () => await this.SaveObject(User));
return task.Result.Content;
The async function can throw an exception if something is wrong with object being saved. In this case my exception will be handled on the client based on it's type. Problem is when async task throws an exception, task.Result.Content buries in within System.AggregateException.
Right now I handle it like this:
try
{
var task = Task.Run(async () => await this.saveObject(User)); return task.Result.Content;
}
catch(AggregateException ex)
{
throw ex.InnerException;
}
I have too many methods that do it the same way. I was wondering if there is a way to avoid using try/catch block in each and every method. Maybe there is an exception filter mechanism, sorta like used on webApi to catch all exceptions within the class in one place? Maybe some attribute I can mark needed methods with?
First off I would recommend that you don't use .Result on a Task. See https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html or https://montemagno.com/c-sharp-developers-stop-calling-dot-result/.
If you follow the above advice and await a Task within a try block, it will throw the actual exception rather than an AggregateException so you might be able to avoid your rethrow code altogether.
Otherwise, if you really want to stick with your .Result code, you can write a generic wrapper method that does common error handling for you:
try
{
var task = Task.Run(async () => await this.Object(User));
return task.Result.Content;
}
catch(AggregateException ex)
{
throw ex.InnerException;
}
To something like:
return RunAsync(() => this.Object(User));
private T RunAsync<T>(Func<Task<T>> func)
{
try
{
var task = Task.Run(func);
return task.Result;
}
catch(AggregateException ex)
{
throw ex.InnerException;
}
}
EDIT:
I've just realised there's another way (see http://blog.stephencleary.com/2014/12/a-tour-of-task-part-6-results.html) which is slightly more "hacky" since it feels more hidden but this:
var task = Task.Run(async () => await this.Object(User));
return task.GetAwaiter().GetResult().Content;
The .GetAwaiter().GetResult() will synchronously wait for the Task (as per .Result) but will not wrap any thrown exceptions in an AggregateException - which seems to be your desire.

Exception in async methods is not caught

The following code does not catch my OperationCancelException which is thrown by calling ct.ThrowIfCancellationRequested.
public partial class TitleWindow : Window, IAsyncInitialization
{
public Task Initialization{get; private set;}
CancellationTokenSource cts;
public TitleWindow()
{
InitializeComponent();
cts = new CancellationTokenSource();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
cts.Cancel();
Initialization = GetCancelExceptionAsync(cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation canceled!");
}
}
public async Task GetCancelExceptionAsync(CancellationToken ct)
{
await Task.Delay(1000);
ct.ThrowIfCancellationRequested();
}
}
However if i replace my Window_Loaded method with the following (making it async and await the call of my async method), the exception gets caught.
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
try
{
cts.Cancel();
await GetCancelExceptionAsync(cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation canceled!");
}
}
Why is my first approach not working? Is the exception not properly propagated to the correct synchronization context?
I was trying to use The Asynchronous Initialization Pattern described in Stephen Clearys blog post to be able to later on await a task which was started in a constructor (and in order to make it comparable to my second example I used the (async) Window_Loaded event to await methods there right away, like suggested to me in a previous question). Then I wanted to provide an option to cancel the async method that I started in the constructor, where i am currently stuck because the exception handling does not work as I expected.
With my "non-working" code, I can catch the exception by putting await Initialization in a try-catch block somewhere, but I still get an additional unhandled exception.
How do I implement this in a way that allows me to await my async method later on (to ensure that I do not work with an inconsistent state of my object) and still being able to cancel that long-running Task (which would of course need to return/set default values)?
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.
Exeption is thrown in the task on another thread.
public async Task GetCancelExceptionAsync(CancellationToken ct)
{
try
{
await Task.Delay(1000);
ct.ThrowIfCancellationRequested();
}
catch (Exception e)
{
// your Cancleation expeption
}
}

I want await to throw AggregateException, not just the first Exception

When awaiting a faulted task (one that has an exception set), await will rethrow the stored exception. If the stored exception is an AggregateException it will rethrow the first and discard the rest.
How can we use await and at the same time throw the original AggregateException so that we do not accidentally lose error information?
Note, that it is of course possible to think of hacky solutions for this (e.g. try-catch around the await, then call Task.Wait). I really wish to find a clean solution. What is the best-practice here?
I thought of using a custom awaiter but the built-in TaskAwaiter contains lots of magic that I'm not sure how to fully reproduce. It calls internal APIs on TPL types. I also do not want to reproduce all of that.
Here is a short repro if you want to play with it:
static void Main()
{
Run().Wait();
}
static async Task Run()
{
Task[] tasks = new[] { CreateTask("ex1"), CreateTask("ex2") };
await Task.WhenAll(tasks);
}
static Task CreateTask(string message)
{
return Task.Factory.StartNew(() => { throw new Exception(message); });
}
Only one of the two exceptions is thrown in Run.
Note, that other questions on Stack Overflow do not address this specific problem. Please be careful when suggesting duplicates.
I disagree with the implication in your question title that await's behavior is undesired. It makes sense in the vast majority of scenarios. In a WhenAll situation, how often do you really need to know all of the error details, as opposed to just one?
The main difficulty with AggregateException is the exception handling, i.e., you lose the ability to catch a particular type.
That said, you can get the behavior you want with an extension method:
public static async Task WithAggregateException(this Task source)
{
try
{
await source.ConfigureAwait(false);
}
catch
{
// source.Exception may be null if the task was canceled.
if (source.Exception == null)
throw;
// EDI preserves the original exception's stack trace, if any.
ExceptionDispatchInfo.Capture(source.Exception).Throw();
}
}
I know I'm late but i found this neat little trick which does what you want. Since the full set of exceptions are available with on awaited Task, calling this Task's Wait or a .Result will throw an aggregate exception.
static void Main(string[] args)
{
var task = Run();
task.Wait();
}
public static async Task Run()
{
Task[] tasks = new[] { CreateTask("ex1"), CreateTask("ex2") };
var compositeTask = Task.WhenAll(tasks);
try
{
await compositeTask.ContinueWith((antecedant) => { }, TaskContinuationOptions.ExecuteSynchronously);
compositeTask.Wait();
}
catch (AggregateException aex)
{
foreach (var ex in aex.InnerExceptions)
{
Console.WriteLine(ex.Message);
}
}
}
static Task CreateTask(string message)
{
return Task.Factory.StartNew(() => { throw new Exception(message); });
}
Here is a shorter implementation of Stephen Cleary's WithAggregateException extension method:
public static async Task WithAggregateException(this Task source)
{
try { await source.ConfigureAwait(false); }
catch when (source.IsCanceled) { throw; }
catch { source.Wait(); }
}
public static async Task<T> WithAggregateException<T>(this Task<T> source)
{
try { return await source.ConfigureAwait(false); }
catch when (source.IsCanceled) { throw; }
catch { return source.Result; }
}
This approach is based on a suggestion by Stephen Toub in this API proposal in GitHub.
Update: I added a special handling of the cancellation case, to prevent the awkwardness of propagating an AggregateException that contains an OperationCanceledException. Now the OperationCanceledException is propagated directly, and the Task.IsCanceled status is preserved. Kudos to #noseratio for pointing out this flaw in the comments of this answer. Of course now this implementation is not much shorter than Stephen Cleary's approach!
Exception Handling (Task Parallel Library)
I could say more but it would just be padding. Play with it, it does work as they say. You just have to be careful.
maybe you want this
God (Jon Skeet) explains await exception handling
(personally i shy away from await, but thats just my preference)
in response to comments (too long for a comment reply)
Then use threads as your starting point for an analogous argument as the best practises there will be the source of ones for here.
Exceptions happily get swallowed unless you implement code to pass them out (for instance the async pattern that the await is preumably wrapping ... you add them to an event args object when you raise an event). When you have a scenario where you fire up an arbitrary number of threads and execute on them you have no control over order or the point at which you terminate each thread. Moreover you would never use this pattern if an error on one was relevant to another. Therefor you are strongly implying that execution of the rest is completley independent - IE you are strongly implying that exceptions on these threads have already been handled as exceptions. If you want to do something beyond handling exceptions in these threads in the threads they occur in (which is bizzarre) you should add them to a locking collection that is passed in by reference - you are no longer considering exceptions as exceptions but as a piece of information - use a concurrent bag, wrap the exception in the info you need to identify the context it came from - which would of been passed into it.
Don't conflate your use cases.
I don't want to give up the practice to only catch the exceptions I expect. This leads me to the following extension method:
public static async Task NoSwallow<TException>(this Task task) where TException : Exception {
try {
await task;
} catch (TException) {
var unexpectedEx = task.Exception
.Flatten()
.InnerExceptions
.FirstOrDefault(ex => !(ex is TException));
if (unexpectedEx != null) {
throw new NotImplementedException(null, unexpectedEx);
} else {
throw task.Exception;
}
}
}
The consuming code could go like this:
try {
await Task.WhenAll(tasks).NoSwallow<MyException>();
catch (AggregateException ex) {
HandleExceptions(ex);
}
A bone-headed exception will have the same effect as in synchronous world, even in case it is thrown concurrently with a MyException by chance. The wrapping with NotImplementedException helps to not loose the original stack trace.
Extension that wraps original aggregation exception and doesn't change return type, so it can still be used with Task<T>
public static Task<T> UnswallowExceptions<T>(this Task<T> t)
=> t.ContinueWith(t => t.IsFaulted ? throw new AggregateException("whatever", t.Exception) : t.Result);
Example:
Task<T[]> RunTasks(Task<T>[] tasks) =>
Task.WhenAll(CreateSometasks()).UnswallowExceptions();
try
{ var result = await CreateTasks(); }
catch(AggregateException ex) { } //ex is original aggregation exception here
NOTE This method will throw if task was canceled, use another approach if cancelling is important for you

Why is this exception not caught?

I'm trying to run the following code:
class Program
{
static void Main(string[] args)
{
var task = Task.Factory.StartNew(() =>
{
throw new ApplicationException("message");
});
try
{
task.ContinueWith(t => Console.WriteLine("End"));
}
catch (AggregateException aex)
{
Console.Write(aex.InnerException.Message);
}
}
}
I expected that the Exception would be caught in the following location:
catch (AggregateException aex)
{
Console.Write(aex.InnerException.Message);
}
But this is not happening. Why is this so?
You're just printing out the task - which won't even have completed yet.
Printing out the task doesn't wait for it to complete, or try to fetch the value.
If you change your code to:
try
{
task.Wait();
}
... then I'd expect it to catch the exception.
(I was previously using Task<T>.Result, but I notice this is a task with no return value, so it would just be the non-generic Task.)
The way Task works, the code that ends up calling the delegate you pass to StartNew will be caught, eventually, and the Exception will be stored in an instance field of the task. That exception can be inspected by looking at the task.Exception property. The line Console.WriteLine(task) is just calling task.ToString internally. That method won't result in the exception being thrown or re-thrown.
However, under certain circumstances the exception that is caught will be re-thrown. Two examples are when accessing Result and calling Wait, as well as when you await a task in C# 5.0.
The following code:
try
{
task.Wait();
}
catch (AggregateException aex)
{
Console.Write(aex.InnerException.Message);
}
Will result in the stored exception being re-thrown and the exception message will be printed.
AggregateException and ApplicationException are both children of the same class, System.Exception.
AggregateException is-not a ApplicationException
Cause your statement is not in the try, but before it... catch will catch every exception from within the try curly brackets...

is it possible to catch when any Task terminates due exception and log?

Is it possible to catch when any Task terminates due exception and log? I've added CurrentDomain_UnhandledException handling but this doesn't help.
I create tasks using Task.Factory.StartNew() as usual. When somewhere inside such task exception occurs it crashes silently (but it supposed to work forever, i'm also using LongRunning option). So I want to be notified about such behavior.
Ideallly I want to set some option somewhere to be notified when any Task crashes due exception.
If it is not possible then likely I should add something to each Task I create? Of course I can just add big try{} finally{} block inside each Task, but probably there are better solutions?
Assuming you have a Test as Task to run:
static int Test()
{
throw new Exception();
}
First Approach - Process exception in the caller's thread:
Task<int> task = new Task<int>(Test);
task.Start();
try
{
task.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine(ex);
}
Note: The exception will be of type AggregateException. All actual exceptions are available through ex.InnerExceptions property.
Second Approach - Process exception in some task's thread:
Define the ExceptionHandler this way:
static void ExceptionHandler(Task<int> task)
{
var ex = task.Exception;
Console.WriteLine(ex);
}
Usage:
Task<int> task = new Task<int>(Test);
task.ContinueWith(ExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
task.Start();
Reference: How to: Handle Exceptions Thrown by Tasks
For tasks that you create yourself, it's reasonably simple: create your own methods which call Task.Factory.StartNew(), but then also call Task.ContinueWith(loggingDelegate, TaskContinuationOptions.OnlyOnFaulted before returning the task.
The problem is that that won't add a fault handler for tasks created by other bits of infrastructure - including by async methods in C# 5. It still might be useful to you though.
You can also use TaskScheduler.UnobservedTaskException, but as per the name that will only be called for exceptions which aren't already observed by something else. (Again, that may be fine for you...)
You can use an extension method that performs an operation when an exception has ocurred.
This happens when the Task gets Faulted. So if it has another tasks to continue with, the next one can check if the previous task was faulted and Log the exception.
I usually use this methods:
//If you want to chain more tasks..
public static Task<T> Continue<T>(this Task<T> task, Action<T> action)
{
if (!task.IsFaulted)
{
task.ContinueWith((t) => action(t.Result), TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion);
}
return task;
}
public static Task OnException(this Task task, Action<Exception> onFaulted)
{
task.ContinueWith(c =>
{
var excetion = c.Exception;
onFaulted(excetion);
},
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously);
return task;
}
So you can use:
Task.Factory.StartNew(...).OnException(ex => Log(ex));
Hope it helps.
Wrap your task.Wait() in a try/catch block and catch AggregateException. Something like this -
Task<string[]> task1 = Task<string[]>.Factory.StartNew(() => GetAllFiles(path));
// Use this line to throw an exception that is not handled.
try
{
task1.Wait();
}
catch (AggregateException ae)
{
ae.Handle((x) =>
{
if (x is UnauthorizedAccessException) // This we know how to handle.
{
Console.WriteLine("You do not have permission to access all folders
in this path.");
Console.WriteLine("See your network administrator or try
another path.");
return true;
}
return false; // Let anything else stop the application.
});
}
Details can be found here - Handle exceptions thrown by Task.
You can create a OnlyOnFaulted continuation on your Task which observes the exception and logs/reports the problem.
t.ContinueWith(task =>
{
// Report and log error
}, System.Threading.CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
The above code will run the task on the UI thread because of TaskScheduler.FromCurrentSynchronizationContext(). This may be necessary if you are using winforms and need to notify the user.

Categories

Resources