---
title: "time.sleep() Blocks the Entire asyncio Event Loop"
aliases: [time-sleep-async, asyncio-blocking, sync-sleep-fastapi, asyncio-sleep]
tags: [python, fastapi, asyncio, concurrency, gotcha, performance]
sources:
  - "daily/2026-04-29.md"
created: 2026-04-29
updated: 2026-04-29
---

# time.sleep() Blocks the Entire asyncio Event Loop

`time.sleep(N)` called inside any `async def` handler freezes the entire asyncio event loop for the duration — no other requests can be processed. It appears to work in local tests (single request) but silently kills concurrency in production.

## Key Points

- `time.sleep()` is a **synchronous blocking call** — it blocks the OS thread, which blocks the asyncio event loop
- `asyncio.sleep()` is the correct replacement — it yields control back to the event loop while waiting
- For CPU-bound or I/O-bound sync code, use `asyncio.run_in_executor()` to run in a threadpool
- Easy to miss because single-request tests always pass — the bug only appears under concurrent load

## Details

### The Problem

```python
# WRONG — blocks all concurrent requests for 5 seconds
@router.get("/process")
async def process_something():
    await do_first_step()
    time.sleep(5)          # ← entire event loop frozen here
    await do_second_step()
    return {"status": "done"}
```

While `time.sleep(5)` runs, every other incoming request waits. On a server handling 50 concurrent users, this compounds into multi-second stalls for every user.

### Correct Replacements

**1. Async wait (most common case):**

```python
import asyncio

@router.get("/process")
async def process_something():
    await do_first_step()
    await asyncio.sleep(5)   # ← yields to event loop, other requests continue
    await do_second_step()
    return {"status": "done"}
```

**2. Sync blocking work in threadpool:**

```python
import asyncio
from concurrent.futures import ThreadPoolExecutor

def heavy_sync_work():
    time.sleep(5)  # or any blocking I/O
    return result

@router.get("/process")
async def process_something():
    loop = asyncio.get_event_loop()
    result = await loop.run_in_executor(None, heavy_sync_work)
    return {"result": result}
```

**3. FastAPI's `def` (not `async def`) routes — automatic threadpool:**

```python
# FastAPI automatically runs non-async routes in a threadpool
@router.get("/process")
def process_something():          # ← no async keyword
    time.sleep(5)                  # safe here — runs in threadpool
    return {"status": "done"}
```

### Code Review Checklist

Search for `time.sleep` as part of any FastAPI code review:

```bash
grep -rn "time\.sleep" app/routers/ app/services/
```

Any hit inside an `async def` function is a bug.

## Related Concepts

- [[wiki/concepts/asyncio-contextvar-task-boundary|asyncio-contextvar-task-boundary]] — other asyncio gotchas in FastAPI
- [[wiki/tech-patterns/fastapi-python-docker|fastapi-python-docker]] — FastAPI best practices

## Sources

- [[daily/2026-04-29.md]] — session 17:51, blocking sleep found in async FastAPI handler
