Skip to content

C# Async Interceptor Null Reference Exception Issue

Problem Description

When using AOP frameworks like Castle DynamicProxy to intercept asynchronous methods, if the interceptor returns prematurely (without calling invocation.Proceed()), for example, by directly returning, methods that return Task<T> will return null. This leads to a NullReferenceException when the caller awaits the result.

Error Characteristics

plain
System.NullReferenceException: Object reference not set to an instance of an object.
   at YourNamespace.YourClass.YourMethod() in YourFile.cs:line XX
  • The exception line points to await someAsyncMethod()
  • The actual problem is that the interceptor returned null
  • It's difficult to pinpoint the root cause through stack traces because the error occurs within the await operation.

Solution

Properly handle the return value of asynchronous methods within the interceptor:

csharp
// Handle return values when the intercepted interface is disabled
if (settings?.IsEnabled == false)
{
    // For methods returning Task<T>
    if (invocation.Method.ReturnType.IsGenericType && 
        invocation.Method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
    {
        var returnType = invocation.Method.ReturnType.GetGenericArguments()[0];
        var defaultValue = returnType.IsValueType ? Activator.CreateInstance(returnType) : null;
        var tcs = typeof(TaskCompletionSource<>).MakeGenericType(returnType);
        var taskCompletionSource = Activator.CreateInstance(tcs);
        tcs.GetMethod("SetResult").Invoke(taskCompletionSource, new[] { defaultValue });
        invocation.ReturnValue = tcs.GetProperty("Task").GetValue(taskCompletionSource);
    }
    // For methods returning Task
    else if (invocation.Method.ReturnType == typeof(Task))
    {
        invocation.ReturnValue = Task.CompletedTask;
    }
    return;
}

Defensive Programming

Check the return value after calling a potentially intercepted method:

csharp
var response = await _service.SomeMethodAsync();
if (response == null)
{
    // Handle cases where the interceptor returns null
    return;
}

Last updated: