cloudflare / cloudflare/workerd
Python Workers: NoGilError race condition during concurrent request initialization (preparePython)
- Dominant language
- C++
- Stars
- 8.7k
- Forks
- 739
- Avg merge
- 2d 20h
- Merged PRs (30d)
- 174
Description
## Bug Description
Python Workers using FastAPI throw `NoGilError: Attempted to use PyProxy when Python GIL not held` when multiple concurrent HTTP requests hit the same isolate. The error occurs in the runtime's `preparePython`/`initPyInstance` code path — not in application code.
## Stack Trace
```
NoGilError: Attempted to use PyProxy when Python GIL not held
at throw_no_gil (pyodide-internal:generated/emscriptenSetup:20688:13)
at wasm://wasm/020fb45e:wasm-function[432]:0x117f9b
at python_getattr (pyodide-internal:generated/emscriptenSetup:23035:13)
at Object.get (pyodide-internal:generated/emscriptenSetup:23106:119)
at async preparePython (pyodide:python-entrypoint-helper:202:28)
at async initPyInstance (pyodide:python-entrypoint-helper:258:24)
at async Proxy. (pyodide:python-entrypoint-helper:300:36)
```
## Reproduction
1. Deploy a Python Worker with FastAPI that makes async HTTP calls
2. Send 3-5 concurrent requests to different endpoints on the same worker
3. Observe `NoGilError` on some requests, with outcome `exception`
The error is more likely when:
- Requests involve CPU-bound work
- Multiple requests arrive within the same ~100ms window
- The isolate is handling concurrent requests in stateless execution mode
## Environment
- `compatibility_date`: `2025-12-01` **NOTE** I just bumped my compat date to `2026-04-01`, so perhaps that will help too.
- `compatibility_flags`: `["python_workers"]`
- Execution model: stateless
- Observed in deployed staging environment (not local dev)
## Analysis
The race condition appears to be in the isolate re-use logic. When a second request arrives while the first is awaiting I/O (GIL released), `preparePython` for the second request tries to access a Python proxy object, but the GIL acquisition fails — likely because the first request's coroutine is resuming or the V8 snapshot restoration leaves the GIL in an inconsistent state.
All application code uses async/await correctly (no threading, no blocking I/O). The error is entirely within `pyodide-internal` and `python-entrypoint-helper`.
Contributor guide
Assessment
This issue has not been assessed yet.