Skip to content

[API Proposal]: DebuggerDisableUserUnhandledAttribute #103105

Closed

Description

@halter73

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;

/// <summary>
/// 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.
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public sealed class DebuggerDisableUserUnhandledExceptionsAttribute : Attribute
{
    public DebuggerDisableUserUnhandledExceptionsAttribute();
}
namespace System.Diagnostics;

public static class Debugger
{
+    /// <summary>
+    /// If a .NET Debugger is attached which supports the BreakForException'API,
+    /// this method will break into the debugger, showing a virtual call stack from the exception.
+    /// </summary>
+    /// <param name="ex">exception object to show</param>
+    public static void BreakForException(Exception ex);
}

API Usage

[MethodImpl(MethodImplOptions.NoInlining)]
[DebuggerDisableUserUnhandledExceptions]
static async Task InvokeUserCode(Func<Task> 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

Activity

  1. added
    api-suggestionEarly API idea and discussion, it is NOT ready for implementation
    on Jun 5, 2024
  2. ghost added
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Jun 5, 2024
  3. terrajobst commented on Jun 6, 2024

    @terrajobst
    Contributor

    I assume the intent is that it would control the default behavior of the debugger for "break when an exception of this type is thrown"? IOW, if the exception still goes unhandled, I'd think the debugger would still break at that point?

  4. added
    blockingMarks issues that we want to fast track in order to unblock other important work
    api-ready-for-reviewAPI is ready for review, it is NOT ready for implementation
    on Jun 6, 2024
  5. added this to the 9.0.0 milestone on Jun 6, 2024
  6. terrajobst commented on Jun 6, 2024

    @terrajobst
    Contributor

    (applying labels to ensure it's high on our backlog)

  7. added and removed
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Jun 6, 2024
  8. dotnet-policy-service commented on Jun 6, 2024

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @tommcdon
    See info in area-owners.md if you want to be subscribed.

  9. removed
    api-suggestionEarly API idea and discussion, it is NOT ready for implementation
    on Jun 6, 2024
  10. added
    enhancementProduct code improvement that does NOT require public API changes/additions
    on Jun 10, 2024
  11. stephentoub commented on Jun 11, 2024

    @stephentoub
    Member

    I'm unclear as to what the state of this proposal is. Is this a feature that the VS debugger has already agreed to, and we're just talking about API name? Or is this actually proposing a feature that still needs agreement / design / etc. at the debugger level?

  12. removed
    blockingMarks issues that we want to fast track in order to unblock other important work
    on Jun 11, 2024
  13. added
    blockingMarks issues that we want to fast track in order to unblock other important work
    on Jul 2, 2024
  14. bartonjs commented on Jul 9, 2024

    @bartonjs
    Member

    Video

    • BreakForException => BreakForUserUnhandledException
    • It was asked if the parameter to BreakForUserUnhandledException could be removed, and just use $exception; but it was felt that the parameter was useful.
    • We discussed "DebuggerDisable" or "DisableDebugger", and feel that the prefix "Debugger" gives better alignment to other Debugger-intent attributes in this namespace.
    • Spell out "ex" to "exception" in the parameter.
    namespace System.Diagnostics;
    
    [AttributeUsage(AttributeTargets.Method)]
    public sealed class DebuggerDisableUserUnhandledExceptionsAttribute : Attribute
    {
        public DebuggerDisableUserUnhandledExceptionsAttribute();
    }
    
    public partial class Debugger
    {
        public static void BreakForUserUnhandledException(Exception exception);
    }
  15. added
    api-approvedAPI was approved in API review, it can be implemented
    and removed
    blockingMarks issues that we want to fast track in order to unblock other important work
    api-ready-for-reviewAPI is ready for review, it is NOT ready for implementation
    on Jul 9, 2024
  16. locked and limited conversation to collaborators on Aug 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-approvedAPI was approved in API review, it can be implementedarea-System.DiagnosticsenhancementProduct code improvement that does NOT require public API changes/additions

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions