Async method in WebApi causes IIS ExecuteRequestHandler to hang
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
When a Web API request appears to hang in IIS at ExecuteRequestHandler, the problem is usually not that async is broken. It is usually that the code looks asynchronous at the controller boundary but blocks somewhere underneath, or that a dependency in the request path is not truly asynchronous.
The Main Cause: Sync over Async
The most common failure pattern is blocking on a Task with .Result, .Wait(), or .GetAwaiter().GetResult() inside the request pipeline. In classic ASP.NET, that can deadlock or tie up request threads badly enough that the request appears stuck in IIS.
This is the anti-pattern:
The controller is synchronous, and the asynchronous operation is forced back into blocking mode. That defeats the point of async and can cause the exact hanging behavior people see in IIS diagnostics.
Make the Entire Request Path Async
The fix is to use await from the controller downward.
But that alone is not enough. If GetByIdAsync internally blocks on something else, the hang can still happen.
Verify Dependencies Are Actually Async
A lot of code is “async in signature only.” For example, a service may return Task<T> but still call a synchronous database API or block on an HTTP request internally.
A correct async downstream call looks more like this:
If the dependency layer is synchronous, the controller's async keyword is only cosmetic.
ConfigureAwait(false) in Library Code
In classic ASP.NET code, ConfigureAwait(false) in library and infrastructure code can reduce the chance of deadlocks by avoiding unnecessary resumption on the request context.
Use it in reusable components, not usually in controller code itself.
That said, ConfigureAwait(false) is not a magical fix for blocking. If you still call .Result, you are still creating the wrong shape of code.
Check IIS Only After Fixing Code Shape
IIS settings can influence how a hang looks, but they are often secondary. Before tuning the server, verify:
- no
.Resultor.Wait()in the request path - no fire-and-forget work that the request accidentally depends on
- async-capable database and HTTP clients are actually being used
- the request is not waiting on a lock or exhausted connection pool
Only after that does it make sense to look at things like request timeouts, app pool recycling, and failed-request tracing.
Instrument the Request Path
If the request still hangs, add timestamps around each awaited boundary. That tells you whether the stall is inside the controller, service, database, or an outbound HTTP call.
Without timing, everything looks like “IIS hung,” even when the real bottleneck is a slow SQL query or an exhausted outbound socket pool.
Resource Starvation Can Mimic Deadlocks
A request can appear hung even when there is no logical deadlock. Examples include:
- thread pool starvation
- database connection pool exhaustion
- creating a new
HttpClientper request and exhausting sockets - long-running CPU work inside the request thread
These issues often surface as async problems because the request never completes, but the actual root cause is capacity or lifecycle management.
Common Pitfalls
A common mistake is making the controller async but keeping synchronous blocking calls in the service or repository layer.
Another mistake is using .Result inside a Web API action because it “works locally.” Local success does not prove it is safe under IIS request context and load.
Developers also often blame IIS first and application code second. In many cases, IIS is only where the stuck request becomes visible.
Finally, avoid creating a new HttpClient for each request. That can cause socket exhaustion and produce symptoms that resemble random hangs.
Summary
- IIS hangs at
ExecuteRequestHandlerare often caused by sync over async blocking. - Use
awaitall the way down the request path. - Verify that dependencies are truly asynchronous, not just wrapped in
Tasksignatures. - Add timing and tracing so you can find the actual stall point.
- Investigate resource starvation and client lifecycle issues before blaming IIS configuration alone.

