AsyncFixer 2.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package AsyncFixer --version 2.0.0
                    
NuGet\Install-Package AsyncFixer -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AsyncFixer" Version="2.0.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AsyncFixer" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="AsyncFixer">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AsyncFixer --version 2.0.0
                    
#r "nuget: AsyncFixer, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AsyncFixer@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AsyncFixer&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=AsyncFixer&version=2.0.0
                    
Install as a Cake Tool

AsyncFixer helps developers in finding and correcting common async/await misuses (i.e., anti-patterns). It currently detects 6 common kinds of async/await misuses and fixes 3 of them via program transformations. AsyncFixer has been tested with thousands of open-source C# projects and successfully handles many corner cases. It is also one of the most common analyzers used in C# projects from Microsoft.

AsyncFixer will work just in the IDE and work as an analyzer on every project you open in Visual Studio. It can also operate in batch mode to correct all misuses in the document, project, or solution. You can download the VSIX from here.

If you want AsyncFixer to deploy as a NuGet package and work as a project-local analyzer that participates in builds, you can use the nuget package. Attaching an analyzer to a project means that the analyzer travels with the project to source control and so it is easy to apply the same rule for the team. You can download the nuget package from here.

Here are async/await misuses (i.e., anti-patterns) that AsyncFixer can currently detect:

AsyncFixer01: Unnecessary async/await usage

There are some async methods where there is no need to use async/await keywords. It is important to detect this kind of misuse because adding the async modifier comes at a price. AsyncFixer automatically removes async/await keywords from those methods.

asyncfixer-1.gif

AsyncFixer02: Long-running or blocking operations inside an async method

Developers use some potentially long-running or blocking operations inside async methods even though there are corresponding asynchronous versions of these methods in .NET or third-party libraries. Some examples for such operations: Task.Wait(), Task.Result, StreamReader.ReadToEnd(), Thread.Sleep(), etc.

AsyncFixer automatically replaces those operations with their corresponding asynchronous operations and inserts an await expression. For instance, it converts Thread.Sleep(...) to await Task.Delay(...).

asyncfixer-2.gif

AsyncFixer03: Fire-and-forget async-void methods and delegates

Some async methods and delegates are fire-and-forget, which return void. Unless a method is only called as an event handler, it must be awaitable. Otherwise, it is a code smell because it complicates control flow and makes error detection/correction difficult. Unhandled exceptions in those async-void methods and delegates will crash the process as well.

AsyncFixer automatically converts void to Task.

asyncfixer-3.gif

AsyncFixer04: Fire-and-forget async call inside an using block

Inside a using block, developers insert a fire-and-forget async call which uses a disposable object as a parameter or target object. It can cause potential exceptions or wrong results. Here is an example:

static void foo()
{
    var newStream = new FileStream("file.txt", FileMode.Create);
    using (var stream = new FileStream("newfile.txt", FileMode.Open))
    {
        newStream.CopyToAsync(stream);
    }
}

We copy the contents of the file to another file above. If the file size is big enough to make CopyToAsync take non-trivial duration, we will have ObjectDisposedException because Stream will be implicitly disposed due to the using block before CopyToAsync is finished. To fix the issue, we need to await asynchronous operations involving disposable objects inside using blocks:

    await newStream.CopyToAsync(stream);

AsyncFixer05: Downcasting from a nested task to an outer task.

Downcasting from a nested task to a task or awaiting a nested task is dangerous. There is no way to wait for and get the result of the child task. This usually occurs when mixing async/await keywords with the old threading APIs such as TaskFactory.StartNew. Here is an example:

async Task foo()
{
    Console.WriteLine("Hello");
    await Task.Factory.StartNew(() => Task.Delay(1000)); // StartNew call returns a nested task: Task<Task>
    Console.WriteLine("World");
}

A developer might expect one-second latency between "Hello" and "World" lines. However, those strings will be printed instantaneously without any latency. The reason is that we await a nested task, which is the return type of StartNew call. When we await the nested task, the return value is the inner task that is the result of Task.Delay call. As we do not await the inner task, we do not see the effect of the delay call. There are three possible fixes:

  1. We can await the inner task as well:
await (await Task.Factory.StartNew(() => Task.Delay(1000)));
  1. We can use Unwrap to expose the inner task to the await expression:
await Task.Factory.StartNew(() => Task.Delay(1000)).Unwrap();
  1. If you do not have reasons to use TaskFactory.StartNew such as TaskCreationOptions and a custom TaskScheduler, we should always use Task.Run to automatically unwrap the inner task.
await Task.Run(() => Task.Delay(1000));

AsyncFixer06: Discarded Task<T> result when converted to Task

When a non-async lambda or delegate returns Task<T> but is assigned to a Func<Task> or similar delegate type expecting Task, the result value is silently discarded. This is because Task<T> implicitly converts to Task, but the generic result is lost.

Note that for async lambdas, the compiler already catches this issue with error CS8031: "Async lambda expression converted to a 'Task' returning delegate cannot return a value. Did you intend to return 'Task<T>'?". However, for non-async lambdas that directly return a Task<T>, there is no compiler warning - the conversion happens silently, which makes this pattern particularly dangerous.

Here is an example:

async Task foo()
{
    Func<Task> fn = () => GetDataAsync(); // GetDataAsync returns Task<string>
    await fn();
    // The string result is silently discarded - no compiler warning!
}

async Task<string> GetDataAsync()
{
    return await httpClient.GetStringAsync("https://example.com");
}

This is dangerous because the developer likely intended to use the returned value. The implicit conversion hides the fact that data is being lost. To fix this issue, there are two possible solutions:

  1. Change the delegate type to match the return type:
Func<Task<string>> fn = () => GetDataAsync();
var result = await fn();
  1. If you truly don't need the result, make it explicit by discarding:
Func<Task> fn = async () => { _ = await GetDataAsync(); };

FAQ

What is the difference between AsyncFixer05 and AsyncFixer06?

Both rules detect task type mismatches, but they address different problems:

Aspect AsyncFixer05 AsyncFixer06
Pattern Task<Task<T>> (nested task) Task<T>Task (implicit conversion)
Problem Awaiting outer task doesn't wait for inner task Result value T is silently discarded
Context Task.Factory.StartNew, Task.Run with async lambdas Lambda/delegate assignments to Func<Task>
Fix Use Unwrap() or double await Change delegate type to Func<Task<T>>

Example comparison:

// AsyncFixer05 - nested task: Task<Task>
Task task = Task.Factory.StartNew(() => DelayAsync());

// AsyncFixer06 - result discarded: Task<string> converted to Task
Func<Task> fn = () => GetDataAsync(); // GetDataAsync returns Task<string>
There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages (6)

Showing the top 5 NuGet packages that depend on AsyncFixer:

Package Downloads
Omnia.Fx

Package Description

Lombiq.Analyzers

Lombiq .NET Analyzers: .NET code analyzers and code convention settings for general .NET projects. See the project website for detailed documentation.

Brupper.Forms

This package contains Brupper to use with Xamarin.Forms

Brupper.Forms.FontAwesome

This package contains Brupper to use with Xamarin.Forms

Brupper.Push

This package contains Brupper to use with Xamarin project for handling push notificcations.

GitHub repositories (37)

Showing the top 20 popular GitHub repositories that depend on AsyncFixer:

Repository Stars
MvvmCross/MvvmCross
The .NET MVVM framework for cross-platform solutions, including Android, iOS, MacCatalyst, macOS, tvOS, WPF, WinUI
exceptionless/Exceptionless
Exceptionless application
ArduPilot/MissionPlanner
Mission Planner Ground Control Station for ArduPilot (c# .net)
FoundatioFx/Foundatio
Pluggable foundation blocks for building distributed apps.
Baseflow/LottieXamarin
Render After Effects animations natively on Android, iOS, MacOS and TvOS for Xamarin
rotorgames/Rg.Plugins.Popup
Xamarin Forms popup plugin
cezarypiatek/MappingGenerator
:arrows_counterclockwise: "AutoMapper" like, Roslyn based, code fix provider that allows to generate mapping code in design time.
Baseflow/XamarinMediaManager
Cross platform Xamarin plugin to play and control Audio and Video
minio/minio-dotnet
MinIO Client SDK for .NET
Baseflow/XF-Material-Library
A Xamarin Forms library for implementing Material Design
dsbenghe/Novell.Directory.Ldap.NETStandard
.NET LDAP client library for .NET Standard >= 2.0, .NET Core >=1.0, NET5/NET6/NET7/NET8 - works with any LDAP protocol compatible directory server (including Microsoft Active Directory).
vknet/vk
Vkontakte API for .NET
exceptionless/Exceptionless.Net
Exceptionless clients for the .NET platform
aaru-dps/Aaru
Aaru Data Preservation Suite
nsnail/NetAdmin
通用后台权限管理系统、快速开发框架(基于C#13/.NET9、Vue3/Vite、ElementPlus等现代技术构建,具有十分整洁、优雅的编码规范)Universal backend permission management system, rapid development framework (based on modern technologies such as C#13/.NET9, Vue3/Vite, ElementPlus, etc., with very neat and elegant coding standards)
titarenko/OAuth2
OAuth2 client implementation for .NET
mehdihadeli/food-delivery-modular-monolith
🌭 A practical and imaginary food and grocery delivery modular monolith, built with .Net 8, Domain-Driven Design, CQRS, Vertical Slice Architecture, Event-Driven Architecture, and the latest technologies.
tpill90/steam-lancache-prefill
CLI tool to automatically prime a Lancache with Steam games
CrossGeeks/FirebasePushNotificationPlugin
Firebase Push Notification Plugin for Xamarin iOS and Android
VahidN/DNTIdentity
A highly customized sample of the ASP.NET Core Identity
Version Downloads Last Updated
2.1.0 2,586 12/29/2025
2.0.0 768 12/28/2025
1.6.0 11,438,212 5/10/2022
1.5.1 3,787,317 1/22/2021
1.3.0 522,477 5/27/2020