Open sandboxFocusImprove this doc

LA0250: Compatibility Level Not Set for OnYield and OnResume Exceptions

Severity Warning
Reported by PostSharp
Message An exception thrown by the OnYield or OnResume advice of the aspect "<Aspect>" in the async method "<Method>" follows the behavior of PostSharp 2026.0: it skips the catch and finally blocks around the await and the OnExit advices. Set the MSBuild property PostSharpCompatibilityLevel to 2027.0 to let it flow as an exception of the await expression, or to 2026.0 to keep this behavior without this warning.

Cause

Since PostSharp 2027.0, the path of an exception thrown by the OnYield(MethodExecutionArgs) or OnResume(MethodExecutionArgs) advice of an async method depends on the PostSharpCompatibilityLevel MSBuild property:

  • At 2027.0 or higher, the exception flows through the method as an exception of the await expression, so the catch and finally blocks of the method and the OnExit advices run.
  • Below 2027.0, or when the property is not set, the exception keeps the behavior of PostSharp 2026.0: it skips the catch and finally blocks around the await and the OnExit advices, and faults the task.

When the property is not set, PostSharp keeps the previous behavior and reports LA0250, so that you choose one. It reports the warning on the async method, once per aspect type that has an OnYield or OnResume advice applied to an async method, including the aspects of the Pattern Libraries. The behavior is the same whether the compiler implements the method with a state machine or with runtime async. The warning is reported whether or not the advices throw.

In the following example, the [Profile] aspect has OnYield and OnResume advices, and is applied to an async method. The @Property(CompatibilityLevel=) directive clears the compatibility level, as a project that does not set PostSharpCompatibilityLevel does.

// @Property(CompatibilityLevel=)

using System;
using System.Threading.Tasks;
using PostSharp.Aspects;
using PostSharp.Serialization;

namespace Errors.LA0250_Error;

[PSerializable]
public class ProfileAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} starts." );

    public override void OnYield( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} is suspended." );

    public override void OnResume( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} resumes." );

    public override void OnExit( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} completes." );
}

public class OrderService
{
    // The project does not set PostSharpCompatibilityLevel, and the aspect has OnYield and OnResume advices.
    [Profile]
    public async Task<int> CountOrdersAsync()
    {
        await Task.Delay( 10 );

        return 3;
    }
}

public static class Program
{
    public static void Main()
    {
        int count = new OrderService().CountOrdersAsync().GetAwaiter().GetResult();
        Console.WriteLine( $"{count} orders." );
    }
}
warning LA0250: LA0250_Error.cs(26,28): An exception thrown by the OnYield or OnResume advice of the aspect "Errors.LA0250_Error.ProfileAttribute" in the async method "Errors.LA0250_Error.OrderService.CountOrdersAsync" follows the behavior of PostSharp 2026.0: it skips the catch and finally blocks around the await and the OnExit advices. Set the MSBuild property PostSharpCompatibilityLevel to 2027.0 to let it flow as an exception of the await expression, or to 2026.0 to keep this behavior without this warning.

Resolution

Set the PostSharpCompatibilityLevel MSBuild property in your project, or for all the projects of a directory in a Directory.Build.props file:

<PropertyGroup>
  <PostSharpCompatibilityLevel>2027.0</PostSharpCompatibilityLevel>
</PropertyGroup>
  • Set it to 2027.0 to adopt the new behavior. An aspect must then tolerate that the method continues after its OnYield or OnResume advice throws, because the user code can catch the exception.
  • Set it to 2026.0 to keep the previous behavior without the warning.

In the following example, the @Property(CompatibilityLevel=2027.0) directive sets the compatibility level as the MSBuild property does, and PostSharp reports no warning.

// @Property(CompatibilityLevel=2027.0)

using System;
using System.Threading.Tasks;
using PostSharp.Aspects;
using PostSharp.Serialization;

namespace Errors.LA0250_Fixed;

[PSerializable]
public class ProfileAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} starts." );

    public override void OnYield( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} is suspended." );

    public override void OnResume( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} resumes." );

    public override void OnExit( MethodExecutionArgs args ) => Console.WriteLine( $"{args.Method.Name} completes." );
}

public class OrderService
{
    // The directive at the top of the file sets the compatibility level to 2027.0, as PostSharpCompatibilityLevel does.
    [Profile]
    public async Task<int> CountOrdersAsync()
    {
        await Task.Delay( 10 );

        return 3;
    }
}

public static class Program
{
    public static void Main()
    {
        int count = new OrderService().CountOrdersAsync().GetAwaiter().GetResult();
        Console.WriteLine( $"{count} orders." );
    }
}
CountOrdersAsync starts.
CountOrdersAsync is suspended.
CountOrdersAsync resumes.
CountOrdersAsync completes.
3 orders.

See Also

Reference

OnYield(MethodExecutionArgs)
OnResume(MethodExecutionArgs)

Other Resources

Semantic Advising of Iterator and Async Methods
Well-Known MSBuild Properties