Debugging with Polly in Visual Studio - App-vNext/Polly GitHub Wiki

Overview

ℹī¸ This documentation describes the previous Polly v7 API. If you are using the new v8 API, please refer to pollydocs.org.

When you define a Polly exception-handling policy, the policy will internally catch matched exceptions and handle them as the policy defines: orchestrate a retry; update circuit-breaker statistics; or channel execution to a fallback.

Debugging such executions can however be noisy in Visual Studio: commonly-used settings cause Visual Studio to break on each exception the policy handles.

A better experience debugging with Polly in the mix can be achieved by configuring Visual Studio debugger settings to break on Just My Code, to continue on so-called user-unhandled exceptions, and by specifying whether to break depending on which module throws the exception.

Understanding the debug setting: Just My Code

First, you likely want to set the debug setting 'Just My Code'.

Visual Studio debugger configuration option: Just My Code

(image via https://docs.microsoft.com/visualstudio/debugger/just-my-code)

This tells the VS debugger that you don't want to step through the code of the .NET implementation, nor of third party libraries you may be using - this includes Polly and third-party libraries you might be calling through Polly.

Configuring the debugger for 'User-unhandled' exceptions

What is 'User-unhandled'?

Next, we need to unpack what Visual Studio means by a User-unhandled exception. The Visual Studio Debugger may break for an exception, saying it is user-unhandled:

Dialog showing Visual Studio breaking on user-unhandled exception

(image via https://docs.microsoft.com/visualstudio/debugger/managing-exceptions-with-the-debugger)

Many have commented that Visual Studio's terminology user-unhandled is confusing.

  • It does not mean the exception is unhandled.
  • It also does not mean your user code won't handle the exception eventually (once code flow has been allowed to continue, via the debugger controls).

Rather, the debugger may break saying an exception is user-unhandled if the exception is first handled by non-user code. Reference: https://blogs.msdn.microsoft.com/visualstudioalm/2015/01/07/understanding-exceptions-while-debugging-with-visual-studio/.

By "user-unhandled", the error message is meaning that user-code didn't handle the exception before it propagated out of user-code into non-user-code.

The debugger breaks by default on 'User-unhandled' exceptions

If an exception is going to be handled by non-user code (for example a Polly policy), why then does the debugger (by default) break for it?

It does this (per MSDN) so that you still have a chance to see the exception at source where it occurred - it is an exception, after all.

However, this behaviour is what can make step-debugging with Polly noisy. For instance, if you have five retries configured and all fail, step-debugging this will break all six times (first try and five retries) the exception is thrown.

You have two options to deal with this:

  • Change the Visual Studio debugger settings to not break on user-unhandled exceptions (see section immediately below)
  • If you control the code where the exception is first thrown, use attributes to tell the debugger not to step through that code (skip down to the section on attributes)

Taming the Visual Studio debugger

To reduce the noise when step-debugging exceptions handled by a Polly policy, in Visual Studio 2015 onwards, you can adjust debugging settings to specify that you do not want to break when a given exception type is 'user-unhandled'.

Follow the instructions here to not break when a specific exception is user-unhandled (ie handled by Polly) in Visual Studio 2015, section Using the Context Menu

Follow the instructions here to not break when a specific exception is user-unhandled (ie handled by Polly) in Visual Studio 2017, section Tell the debugger to continue on user-unhandled exceptions

For example, when using a Polly policy to handle System.Io.IoException, you might specify not to break when this is 'user-unhandled' (as we learnt above, this includes 'handled by Polly').

This approach can have limitations, in that you might want the debugger not to break on IoExceptions on that particular code path; but you might still want it to break if those exceptions are thrown elsewhere. Visual Studio 2015 does not give you that level of control, but Visual Studio 2017 comes closer to doing so. With Visual Studio 2017, you can specify to break or not break according to conditions: for example according to which module is throwing.

Dialog showing configuring Visual Studio 2017 conditions for breaking on an exception

Image via https://docs.microsoft.com/visualstudio/debugger/managing-exceptions-with-the-debugger: Follow the instructions under Add conditions to an exception

If for example you are using Polly to handle exceptions thrown by a third-party library or particular System.*.dll, you could use Visual Studio's fine control to specify that the debugger should not break when the exception is thrown by that library. However, if the exception was unexpectedly thrown elsewhere in your code, the debugger would continue to break.

The [DebuggerNonUserCode] and [DebuggerStepThrough] attributes

If you control the code where the exception is being thrown, another way to smooth the debugging experience is to add the [DebuggerStepThrough] or [DebuggerNonUserCode] attribute to methods where the exception is thrown - this is an alternative to configuring the debugger to continue on so-called user-unhandled exceptions.

Note that way back in Visual Studio 2015, this behaviour got broken, as this issue explored. It is confirmed working again in Visual Studio 2019.

Second-chance and unhandled exceptions

Breaking on a 'first-chance' (or user-unhandled first-chance) exception means that the debugger breaks at the first chance after the exception is thrown (if configured to do so), even if you do have a try/catch that will handle that exception later.

For completeness, an exception not handled by a try/catch is known as a 'second-chance' or 'last-chance' exception. If there is no code handling for 'second-chance' or 'last-chance' exceptions, the debugger will always break on them.

Source Link support

From v7.10, Polly supports Source Link. This means that you can step into Polly code (if useful) as you debug. To enable this, in Visual Studio, in Tools -> Options -> Debugging:

  • uncheck Enable Just my code
  • check Enable Source Link support.