[API Proposal]: DebuggerDisableUserUnhandledAttribute · Issue #103105 · dotnet/runtime (original) (raw)

Background and motivation

In .NET 9, Visual Studio is adding support for catching async user-unhandled exceptions which will be enabled by default. This feature has existed for a long time for synchronous methods, but not for async methods. There are several exceptions in ASP.NET Core that propagate through user code but are expected to be handled by framework code. One example is the NavigationException used to force redirects when calling NavigationManager.NavigateTo() during static Blazor rendering.

Async user unhandled exception

The proposal is to add an attribute that can be added to framework methods that handle exceptions that propagate through user code like Blazors NavigationException and a Debugger method that these attributed framework methods can use to indicate the exception should really be treated as user unhandled. This gives the framework the opportunity to do runtime checks on the exception and run it through dynamic filtering logic before deciding if the exception should really be considered user-unhandled.

API Proposal

namespace System.Diagnostics;

///

/// If a .NET Debugger is attached which supports the BreakForException API, /// this attribute will prevent the debugger from breaking on user-unhandled exceptions /// when the exception is caught by a method with this attribute, unless BreakForException is called. /// [AttributeUsage(AttributeTargets.Method)] public sealed class DebuggerDisableUserUnhandledExceptionsAttribute : Attribute { public DebuggerDisableUserUnhandledExceptionsAttribute(); }

namespace System.Diagnostics;

public static class Debugger {

}

API Usage

[MethodImpl(MethodImplOptions.NoInlining)] [DebuggerDisableUserUnhandledExceptions] static async Task InvokeUserCode(Func userCode) { try { await userCode(); } catch (Exception ex) { if (TryHandleWithFilter(ex)) { return; // example case where we don't want to break for user-unhandled exceptions }

    Debugger.BreakForException(e); // debugger will stop here and show the exception if attached.
}

}

Alternative Designs

If other frameworks or libraries do not need this, the VS debugger could special case these ASP.NET Core exceptions.

It might also be easier in some cases for framework and library developers if they could add an attribute to the Exception type itself if it's a type should always be handled by the framework like Blazor's NavigationException.

namespace System.Diagnostics;

[AttributeUsage(AttributeTargets.Class)] public sealed class DebuggerDisableUserUnhandledAttribute : Attribute { public DebuggerDisableUserUnhandledAttribute(); }

using System.Diagnostics;

namespace Microsoft.AspNetCore.Components;

[DebuggerDisableUserUnhandled] public class NavigationException : Exception { // .... }

However, this might be too inflexible because it's all or nothing in terms of whether a given exception type should be considered "user unhandled" regardless of what method threw it or what non-user methods are on the stack that could handle it. This would not work as well as the primary proposal when the exception is handled by an exception "filter" or "handler" that's called after the exception is caught and therefore is not on the stack.

Risks

This use case may be too niche to warrant new runtime API. This might also make it more confusing to developers who expect the debugger to break every user-unhandled exception propagating through user code even if the framework gracefully handles it.

@gregg-miskelly