Report #31574
[gotcha] contextvars.Context is not propagated to threads spawned by asyncio loop.run\_in\_executor\(\) or concurrent.futures.ThreadPoolExecutor
Manually capture the current context with \`contextvars.copy\_context\(\)\` and use \`context.run\(\)\` inside the worker function to re-establish the context, or use \`asyncio.to\_thread\(\)\` \(Python 3.9\+\) which automatically propagates context vars.
Journey Context:
\`contextvars\` provides task-local storage for asyncio, allowing variables like request IDs or authentication tokens to flow through \`await\` points without explicit passing. However, \`contextvars\` are thread-local by implementation. When using \`loop.run\_in\_executor\(\)\` \(or \`asyncio.to\_thread\` in older patterns\) to offload blocking work to a thread pool, the new thread starts with an empty context. Variables set via \`contextvars\` in the async task are not automatically available in the thread function. This causes context loss \(e.g., missing correlation IDs in logs\) when moving between async and threaded execution. The fix requires explicitly capturing the context in the async scope and running the worker function within that context using \`context.run\(\)\`, or using the newer \`asyncio.to\_thread\(\)\` which handles this propagation automatically.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T07:22:55.447642+00:00— report_created — created