dotnet/BenchmarkDotNet · error · NotSupportedException

async void is not supported by design

Error message

async void is not supported by design

What it means

During harness source generation, CodeGenerator inspects the benchmark method's return type. A method with void return type but the AsyncStateMachineAttribute is an `async void` method, which BenchmarkDotNet deliberately does not support: the engine cannot await or measure it because async void gives no handle to the completion. NotSupportedException is thrown at build time.

Source

Thrown at src/BenchmarkDotNet/Code/CodeGenerator.cs:133

            if (method.ReturnType.IsAwaitable(out var awaitableInfo))
            {
                if (benchmark.Job.ResolveValue(RunMode.ConsumeTasksSynchronouslyCharacteristic, EnvironmentResolver.Instance)
                    && AwaitHelper.IsBuiltInTaskType(method.ReturnType))
                {
                    return new SyncTaskDeclarationsProvider(benchmark);
                }
                return new AsyncDeclarationsProvider(benchmark, awaitableInfo.ResultType);
            }

            if (method.ReturnType.IsAsyncEnumerable(out var asyncEnumerableInfo))
            {
                return new AsyncEnumerableDeclarationsProvider(benchmark, asyncEnumerableInfo.ItemType, asyncEnumerableInfo.MoveNextAsyncMethod.ReturnType);
            }

            if (method.ReturnType == typeof(void) && method.HasAttribute<AsyncStateMachineAttribute>())
            {
                throw new NotSupportedException("async void is not supported by design");
            }

            return new SyncDeclarationsProvider(benchmark);
        }

        // internal for tests

        internal static string GetParamsContent(BenchmarkCase benchmarkCase)
            => string.Join(
                string.Empty,
                benchmarkCase.Parameters.Items
                    .Where(parameter => !parameter.IsArgument)
                    .Select(parameter => $"{(parameter.IsStatic ? benchmarkCase.Descriptor.Type.GetCorrectCSharpTypeName() : "base")}.{parameter.Name} = {parameter.ToSourceCode()};"));

        internal static string GetCancellationTokenAssignment(BenchmarkCase benchmarkCase)
        {
            var targetType = benchmarkCase.Descriptor.Type;
            var cancellationTokenMembers = new System.Collections.Generic.List<string>();

View on GitHub (pinned to b515068b61)

Solutions

  1. Change the benchmark to return Task (or ValueTask): `[Benchmark] public async Task Foo()`. BDN supports Task/ValueTask and IAsyncEnumerable<T>.
  2. If the work is genuinely fire-and-forget, wrap it so it returns Task and await inside.
  3. Remove async and run synchronously if async measurement is not required.
  4. Ensure no base/overload accidentally reintroduces an async void signature.

Example fix

// before
[Benchmark]
public async void Foo() { await DoWork(); }

// after
[Benchmark]
public async Task Foo() { await DoWork(); }
Defensive patterns

Strategy: validation

Validate before calling

// Compile-time: never declare async void benchmarks. Static guard example:
static bool IsValidBenchmarkMethod(MethodInfo m)
    => !(m.ReturnType == typeof(void) && m.GetCustomAttribute<AsyncStateMachineAttribute>() != null);

Type guard

// rejects async void benchmark methods
static bool IsNotAsyncVoid(MethodInfo m)
    => !(m.ReturnType == typeof(void) && m.GetCustomAttribute<AsyncStateMachineAttribute>() is not null);

Try / catch

try { CodeGenerator.GetDeclarationsProvider(benchmark); }
catch (NotSupportedException ex) when (ex.Message.Contains("async void"))
{
    // surface a build error telling the user to change the return type to Task
}

Prevention

When it happens

Trigger: Declaring a benchmark as `[Benchmark] public async void Foo() { await ... }`. The compiler emits AsyncStateMachineAttribute on a void-returning async method, hitting the guard.

Common situations: Copying an async helper as a benchmark without changing its signature, or assuming async benchmarks can be void like event handlers.

Related errors


AI-assisted analysis of dotnet/BenchmarkDotNet@b515068b61 (2026-08-13). Data as JSON: /api/errors/7536495791859dbb. Report an issue: GitHub.