36

As indicated here and here, exceptions occuring in an async Task are technically not unhandled.

This is particularly nasty when working with MVC. It actually took us a while to figure out why it was occurring more and more that exceptions weren't being caught, we had been gradually introducing Web API calls to our aplication the past few weeks.

public async Task<ActionResult> Foo()
{
    // ...
}

The suggested workaround is to make VS break on all exceptions instead of only unhandled exceptions. It works, with the annoying 'side-effect' that it does indeed break on all exceptions :)

Is there another workaround that doesn't involve breaking on all exceptions? It can be specific to MVC but doesn't have to be (meaning if it's a general solution that happens to work for MVC).

Community
  • 1
  • 1
user247702
  • 23,641
  • 15
  • 110
  • 157
  • 1
    You might want to vote on uservoice for this: https://visualstudio.uservoice.com/forums/121579-visual-studio-ide/suggestions/35620360-break-debugger-on-exception-throws-from-async-meth – Liero Oct 03 '18 at 14:00

5 Answers5

3

A) Wrap your calls and throw a custom Exception in your Task code. Break on only throw of your custom exception. You can select the Exceptions for first throw.

B). Debug.Assert() your Task Results, if you have any wait code. i.e., not just firing and forgetting. Tasks return the Exceptions in a property if you wait on them somewhere or stick error handling in a continuation.

psuedo code i.e. task. continuewith(r => if(!r.Exception is null) Debug.Break())) etc.

Hope that helps you on the right path.

  • I like the `Debug.Assert` idea, but I don't see a way to use either of these ideas in the context of ASP.NET MVC, where you have `public async Task Foo()`. – user247702 Oct 07 '13 at 15:04
  • The simple way would be this. public async Task Foo(){try{ your code}catch{if(Debugger.IsAttached)Debug.Break();}} – CodeMonkeyForHire Oct 07 '13 at 19:14
3

The try-catch reference in MSDN has some guidance and examples on exceptions in async methods, and states: "The completed task to which await is applied might be in a faulted state because of an unhandled exception in the method that returns the task. Awaiting the task throws an exception."

For the example given on that page, it states: "The following example illustrates exception handling for async methods. To catch an exception that an async task throws, place the await expression in a try block, and catch the exception in a catch block. Uncomment the throw new Exception line in the example to demonstrate exception handling. The task's IsFaulted property is set to True, the task's Exception.InnerException property is set to the exception, and the exception is caught in the catch block."

Here's the copy from the example given there:

public async Task DoSomethingAsync()
{
    Task<string> theTask = DelayAsync();

    try
    {
        string result = await theTask;
        Debug.WriteLine("Result: " + result);
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Exception Message: " + ex.Message);
    }
    Debug.WriteLine("Task IsCanceled: " + theTask.IsCanceled);
    Debug.WriteLine("Task IsFaulted:  " + theTask.IsFaulted);
    if (theTask.Exception != null)
    {
        Debug.WriteLine("Task Exception Message: "
            + theTask.Exception.Message);
        Debug.WriteLine("Task Inner Exception Message: "
            + theTask.Exception.InnerException.Message);
    }
}

private async Task<string> DelayAsync()
{
    await Task.Delay(100);

    // Uncomment each of the following lines to 
    // demonstrate exception handling. 

    //throw new OperationCanceledException("canceled");
    //throw new Exception("Something happened.");
    return "Done";
}

// Output when no exception is thrown in the awaited method: 
//   Result: Done 
//   Task IsCanceled: False 
//   Task IsFaulted:  False 

// Output when an Exception is thrown in the awaited method: 
//   Exception Message: Something happened. 
//   Task IsCanceled: False 
//   Task IsFaulted:  True 
//   Task Exception Message: One or more errors occurred. 
//   Task Inner Exception Message: Something happened. 

// Output when a OperationCanceledException or TaskCanceledException 
// is thrown in the awaited method: 
//   Exception Message: canceled 
//   Task IsCanceled: True 
//   Task IsFaulted:  False
Krishna Gupta
  • 2,025
  • 19
  • 20
  • 7
    What does catching exceptions programmatically have to do with VS breaking on exceptions, like OP asked? – Rudey Jan 07 '17 at 18:14
2

You could try and listen for this event if nothing else works here

AppDomain.CurrentDomain.UnhandledException

or

AppDomain.CurrentDomain.FirstChanceException

Then you need to put some if-constructs (check what sender for example) to hit your breakpoint only when it makes sense.

CSharpie
  • 9,195
  • 4
  • 44
  • 71
1

Enabling "Just My Code" is one way to do this, as suggested here. True, the exceptions are technically not unhandled, but they are unhandled by user code, which gets VS to do the useful thing and show you where the exception occurred.

See this answer for some screenshots.

Obviously this setting has other implications (e.g. you can no longer step through framework code), so this may or may not be suitable.

Community
  • 1
  • 1
Roman Starkov
  • 59,298
  • 38
  • 251
  • 324
0

You could add a handler for TaskScheduler.UnobservedTaskException.

However, perhaps you've already tried that and you want a way to make the debugger break at the original exception rather than in the unobserved task exception handler.

Olly
  • 5,966
  • 31
  • 60