Report #16560
[bug\_fix] Asyncio no current event loop in thread
The root cause is that asyncio event loops are thread-local. When a new thread is created \(e.g., via \`threading.Thread\`\), it has no event loop running. Calling \`asyncio.get\_event\_loop\(\)\` \(deprecated behavior\) or trying to run a coroutine via \`asyncio.run\_coroutine\_threadsafe\` without a loop in the current thread raises \`RuntimeError\`. The fix is to explicitly create and set a new event loop in the thread target function using \`asyncio.run\(coro\(\)\)\` \(which handles creation and cleanup automatically\) or manually with \`loop = asyncio.new\_event\_loop\(\); asyncio.set\_event\_loop\(loop\); loop.run\_until\_complete\(coro\(\)\)\`.
Journey Context:
You have a FastAPI app that uses a background thread to process a heavy CPU task. Inside that thread, you need to call an async function \`async def fetch\_data\(\): ...\` to query a database using \`asyncpg\`. You write:
\`\`\`python
def thread\_target\(\):
result = asyncio.run\(fetch\_data\(\)\)
print\(result\)
thread = threading.Thread\(target=thread\_target\)
thread.start\(\)
\`\`\`
This works. However, you later refactor to use a shared event loop for 'efficiency' and write:
\`\`\`python
loop = asyncio.get\_event\_loop\(\) \# Gets main thread's loop
def thread\_target\(\):
future = asyncio.run\_coroutine\_threadsafe\(fetch\_data\(\), loop\)
return future.result\(\)
\`\`\`
This crashes with \`RuntimeError: There is no current event loop in thread 'Thread-1'\`. You realize that \`asyncio.get\_event\_loop\(\)\` in Python 3.10\+ behaves differently and that you cannot access the main loop from a thread without passing it. You learn that \`asyncio.run\_coroutine\_threadsafe\` requires the loop to be passed explicitly, but the error actually happens when you try to get the loop inside the thread. The 'aha' moment is understanding that each thread manages its own event loop policy; by default, a new thread has no loop associated with it. To run async code in a thread, you must create a new loop specifically for that thread's context using \`asyncio.new\_event\_loop\(\)\` and \`set\_event\_loop\(\)\`, or simply use \`asyncio.run\(\)\` which does this setup and teardown automatically per thread.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T02:55:16.122361+00:00— report_created — created