How do I fix a deadlock in async code?

Updated October 2026 · How we answer

Short answerFix a deadlock by ensuring locks are acquired in a consistent order, using timeouts, or avoiding blocking calls inside async functions that hold a lock.

Common causes in async code

In async code, deadlocks often happen when two or more tasks each hold a resource and wait for another resource held by the other. For example, Task A holds lock 1 and awaits lock 2, while Task B holds lock 2 and awaits lock 1. Neither can proceed.

Another frequent cause is mixing synchronous blocking calls (like fs.readFileSync or a busy loop) inside an async function that holds a lock. This blocks the event loop, preventing other tasks from releasing the lock they hold.

  • Acquiring multiple locks in different orders
  • Awaiting a Promise that never resolves while holding a lock
  • Using a mutex that is not released on error paths
  • Blocking the event loop with synchronous I/O
  • Recursive locking without reentrancy support

How to prevent and fix

The most reliable fix is to enforce a global lock ordering: always acquire locks in the same sequence across your codebase. If that is not possible, use a lock with a timeout so a task can give up and retry, or use a single lock for related resources.

Also ensure locks are released in a finally block so errors do not leave them held. Avoid holding a lock across an await if you can restructure the code to do the critical work synchronously, or use a queue to serialize access.

  • Define a strict order for acquiring locks and follow it everywhere
  • Use lock timeouts and retry with backoff
  • Release locks in a finally block
  • Minimize the critical section; do not await inside it if possible
  • Use a single lock or a queue for related operations
  • Consider higher-level abstractions like a database transaction

Common mistakes

  • Assuming that async/await prevents deadlocks; it can still occur when multiple locks are involved.
  • Forgetting to release a lock when an error is thrown, causing other tasks to wait forever.
  • Using a mutex that is not reentrant and then trying to lock it again in the same task.
From our shopsCaseMorph: Type an idea, see a custom phone case in seconds, then print a one-of-one.