11

I'm struggling to figure out how to use visual studio to debug async code sanely, since it's not breaking where I want it to. For example, in this code:

using System;
using System.Threading.Tasks;

namespace TestNS{
    class Program {
        static void Main(string[] args) {
            Foo().Wait(); // sadly, it breaks here
        }

        static async Task Foo() {
            var foo = 42;
            if (foo == 42) {
                throw new Exception(); // want it to break here
            }
        }
    }
}

How can I get Visual Studio to automatically break where the exception is thrown and not at the top of my program? Or equivalently, without restarting the process, can I jump to that context and see the values of those locals?

I understand the root problem is that the exceptions are technically being handled by the behind the scenes async code, so VS doesn't see them as being unhandled. Surely no one else likes this behavior though, and there's a way to tell VS that exceptions that bubble up to an async call aren't really caught... I've tried turning "just my code", "break when this exception is thrown", etc., on and off, but nothing helps.

I am not looking for a solution that includes editing my code or manually adding breakpoints. Similarly, handing certain exceptions in special ways is also not a real solution. Not all the exceptions I want to break on are from my own code; often it will come from a missing key in a dictionary, or something like that. Unexpected exceptions happen all the time when developing code, and I want to see that program state easily without manually placing breakpoints and/or try/catch code, rerunning the program, manually advancing the program until I happen to get to the function. And, that's all under the assumption I can even get it to trigger again, when I don't know anything about why it happened in the first place. And then do that for every exception that ever might occur during development.

On top of all that, if you're looking at the AggregateException that does come back, there isn't even a nice way to figure out where that exception comes from. You've got to "view details", then expand the exception, expand the inner exception, hover over the stack trace, remember the file and line number, close the details, go to the right place manually, and now put manual breakpoints and go debug manually. Why can't the system use this stack trace to do the right thing?

I'm putting all these details in because I have read 20 other SO posts about similar topics. They don't put all these details in, and people seem to go off happily, so I'm confused what I'm doing wrong. None of this happens when developing non-async code. Are all of the steps I'm doing above necessary? What is the best practice to make writing async code as pleasant as writing non-async code in terms of examining exception context?

4
  • did you try ticking checkbox Exception Settings --> Common Language Runtime? in visual studio? Commented Mar 22, 2019 at 23:12
  • 2
    static async Task Main(string[] args), and await your method call instead of using Wait() Commented Mar 22, 2019 at 23:15
  • @BradleyUffner after doing that, the behavior is actually worse. The application breaks, but there is no code at all to show Commented Mar 22, 2019 at 23:26
  • @ChuckyEllison see my answer, you most probably doesn't have configured your project to the latest c# standard, which supports async main (7.1) Commented Mar 23, 2019 at 0:02

2 Answers 2

20

you need to activate Common Language Runtime exception using CTRL + ALT + E as in the picture below .

enter image description here

Sign up to request clarification or add additional context in comments.

3 Comments

I only had some of them selected. Selecting all of them seems to be what I want. Hopefully I don't run into a situation where I'm debugging code that throws those kinds of exceptions in normal operation, but this is waaaay better than what I was doing before. Thanks!
I think it should be important to explain what ticking these boxes does. According to the documentation, any first chance exception whose type is ticked in the Exception Settings panel will make the debugger break. First chance exceptions are these handled through a Try ... Catch mechanism. They oppose to unhandled exceptions, which are never caught down the stack.
So ticking these boxes works because an AggregateException captures the inner exceptions and therefore keeps them as first chance exceptions, which by default do not make the debugger to break.
0

You need to enable Common Language Runtime exceptions as @sayah already answered.

That said, you should use async all the way up:

static async Task Main(string[] args) {
    await Foo();
}

This will show you the exceptions, where they occour.

Stephen Cleary wrote a good article on the reasons why you want to use async to top.

Take a look at this example:

internal class Program
{
    //static void Main(string[] args)
    //{
    //    try
    //    {
    //        Foo().Wait();
    //    }
    //    catch (FooException)
    //    {
    //        Console.WriteLine("Yep, its a Foo Exception");
    //    }
    //    catch (AggregateException)
    //    {

    //        Console.WriteLine("Damn it, we got an Aggregate");
    //    }

    //    Console.ReadKey();
    //}

    static async Task Main(string[] args)
    {
        try
        {
            await Foo();
        }
        catch (FooException)
        {
            Console.WriteLine("Yep, its a Foo Exception");
        }
        catch (AggregateException)
        {

            Console.WriteLine("Damn it, we got an Aggregate");
        }

        Console.ReadKey();
    }


    class FooException : Exception
    {
    }

    static async Task<int> Foo()
    {
        await Task.Delay(100);
        var foo = 42;
        if (foo == 42) {
            throw new FooException(); // want it to break here
        }

        return 43;
    }
}

If you exchange the commented Main(), you will see, that you get an AggregateException in the non async method, while you get an FooException in the async method.


Note: If this doesn't work for you, you need to set the language level in project's advanced build settings to C# 7.1 or later, as explained here.

  • Right click your project, and click Properties

Build

  • Go to build settings and click advanced, to set the language version to C# 7.1 or later

Advanced

2 Comments

I tried this as mentioned above, and the behavior is actually worse. The application breaks, but there is no code at all to show in the debugger. Try unchecking the "Common Language Runtime" in "break when thrown" and you should see the same behavior. The problem is "break when thrown" not async all the way up.
Yeah just tested, and you are right. Not sure about why... If I recall correct, it worked the last time I tried, but it was a more complex application. Anyway, you should do it the async, see my example above @ChuckyEllison

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.