Report #15356
[gotcha] Contextvars lost when submitting to concurrent.futures.ThreadPoolExecutor
Manually copy the context and run the function inside it: \`ctx = contextvars.copy\_context\(\); future = executor.submit\(ctx.run, func, \*args\)\`. For asyncio interop, use \`asyncio.to\_thread\` \(Python 3.11\+\) which preserves contextvars automatically, or wrap the sync function to explicitly copy context.
Journey Context:
ThreadPoolExecutor predates \`contextvars\` \(3.7\) and was never updated to propagate them. When you submit work to a thread pool, the worker thread starts with a blank context, breaking request tracing, logging contexts, or any \`contextvars\` usage. This is especially insidious when offloading CPU-bound work from an async endpoint—the context \(like request ID\) vanishes silently. The fix requires capturing the context in the submitting thread and running the callable within that context in the worker.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T23:50:57.993125+00:00— report_created — created