Search code examples
exceptionvisual-studio-2013async-awaitvisual-studio-debugging

Async exception not caught by debugger


I'm working on an MVC5 project in VS2013. I seem to be finding that most (but not all) of my exceptions are being ignored by the debugger and as a result I end up with the exception and stack trace simply being written to the browser, precluding any examination of the objects involved in the exception.

For instance - I deliberately code an exception to prove the point:

    <Authorize(Roles:="IdentityAdmin")>
Public Async Function Import(model As RegisterViewModel) As Task(Of ActionResult)
    Dim a As Object = "he"
    Dim b As Integer = a

Clearly the last line will throw a 'type mismatch' exception which I think should result in the debugger halting execution, highlighting the error in the VS2013 UI to enable me to examine the various objects and determine the problem.

Instead I simply find myself with the browser detailing the exception and VS2013 unresponsive:

Server Error in '/' Application.

Input string was not in a correct format.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.FormatException: Input string was not in a correct format.

Source Error:

Line 291: If Db.Users.Find(acct.username) Is Nothing Then

Line 292: Dim a As Object = "he"

Line 293: Dim b As Integer = a

When I insert the same exception-generating code into a non-async part of the code the VS debugger does catch the exception - so I am guessing this is an issue with debugging async code. Is it really the case that the VS debugger can't catch these exceptions?

UPDATE

After further searching I came across a suggestion to disable 'Just My Code' and manually enable various types of exception. There was the expected hailstorm of First Chance Exceptions most of which I could tune out by disabling certain exceptions. But this DID 'fix' the behaviour described above. It seems that the debugger is regarding my child async threads as 'Not My Code'. Slightly baffled but I guess this could be an answer of sorts?


Solution

  • Disabling Just My Code should not make a difference in this case, enabling "Break when an exception is thrown" is what you want to make the debugger stop at the correct place.

    The issue is that when you make the method Async, it runs in a background Task. The Task will catch any exceptions that occur in the code it is executing and re-throw that exception to whatever uses the result of the Task. So for example if you have the following MVC code

    Async Function Index() As Task(Of ActionResult)
        Dim n as Integer = Await Method1()
        Return View()
    End Function
    
    Async Function Method1() As Task(Of Integer)
       Dim a As Integer = 0
       Dim b As Integer = 1 / b
       Return b
    End Function
    

    The Task executing the code inside of Method1 will catch the exception, and throw it in Index since it is using the result of Method1, then the Task executing Index will catch that exception and re-throw it to the MVC framework code that is using the result of Index, and then the MVC framework handles the exception and displays the error page.

    When Just My Code is enabled and methods are executing synchronously the debugger will stop when an exception propagates out of user code with the message that it was "user unhandled". In the case above if an exception propagates from Index to the MVC framework which is not your code, the debugger will stop when JMC is enabled. Since in the case of an asynchronous method the exception is not unhandled but caught by the Task, it does not cross out of user code so disabling Just My code will not make a difference.

    The reverse of this is, if an exception occurs in your synchronous method and you have disabled Just My Code, the debugger would not stop anyway, because there is no notion of "User Unhandled" when JMC is disabled and the MVC framework will ultimately handle that exception.