Report #6167
[gotcha] Data leakage between tasks when using threading.local\(\) with ThreadPoolExecutor
Never assume threading.local\(\) is clean at task start. Explicitly reset all thread-local state at the beginning of your task function, or use a context manager to clear/set state, or avoid threading.local\(\) entirely in favor of passing context explicitly through function arguments.
Journey Context:
ThreadPoolExecutor maintains a pool of worker threads to avoid the overhead of thread creation. When a task completes, the thread returns to the pool and waits for the next task. \`threading.local\(\)\` stores data that is \*per-thread\*, meaning it persists as long as the thread exists. Therefore, values set in \`threading.local\(\)\` by Task A will still be present when the same worker thread picks up Task B. This causes silent data leakage and extremely difficult-to-debug heisenbugs, especially when the local storage includes request-specific state like user IDs or database connections. The assumption that 'local' means 'task-local' is wrong; it means 'thread-local' and threads are reused.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-15T23:17:14.016161+00:00— report_created — created