BLOG · C# · BEST PRACTICES

8 async/await mistakes that crash your C# applications (and how to fix them)

Illustration: the await keyword in pink inside a code window

Many production crashes come from a single source: misused async/await. And it can rot any C# app.

We've all been there: joining a project and discovering that async is badly implemented. Production deadlocks, swallowed exceptions, unpredictable behavior... Here are 8 mistakes that turn your C# code into a bug generator.

1. Not awaiting an async method

The method starts but your code moves on without waiting. Exceptions are swallowed and the result is ignored.

// ❌ SomeAsyncMethod();
// ✅ await SomeAsyncMethod();

2. Using async void instead of async Task

These methods can't be awaited, exceptions surface differently and cause crashes.

// ❌ async void MyMethod()
// ✅ async Task MyMethod()

3. Blocking with .Result or .Wait()

High deadlock risk, especially in UI and ASP.NET applications.

// ❌ var result = SomeAsyncMethod().Result;
// ✅ var result = await SomeAsyncMethod();

4. Ignoring the synchronization context

Useless hops back to the main thread (especially in non-UI code) hurt performance.

// ❌ await SomeLongRunningMethodAsync();
// ✅ await SomeLongRunningMethodAsync().ConfigureAwait(false);

5. Mishandling async exceptions

Lost debugging information and unpredictable behavior. A bare try/catch without proper propagation isn't enough: capture AND propagate exceptions.

6. Overusing async for synchronous operations

Useless overhead that degrades application performance.

// ❌ async Task<int> AddAsync(int a, int b) { return await Task.FromResult(a + b); }
// ✅ int Add(int a, int b) { return a + b; }

7. Using Task.Run for I/O work

Wasting threads on operations that are already non-blocking by nature.

// ❌ await Task.Run(() => SomeIOMethod());
// ✅ await SomeIOMethodAsync();

8. Forgetting to return a Task

Calling code can't know when the operation finished, nor handle its errors.

// ❌ async void DoSomethingAsync() { SomeOtherAsyncMethod(); }
// ✅ async Task DoSomethingAsync() { await SomeOtherAsyncMethod(); }

Takeaway: each of these mistakes may look minor, yet any of them can crash your app in production. Avoiding them isn't just cleaner code: it protects your business and your sanity.

This article comes from one of my LinkedIn posts (in French): join the discussion ↗