Skip to content

fix: propagate ContextVar state to async_execution worker threads#4834

Open
SinzoL wants to merge 1 commit intocrewAIInc:mainfrom
SinzoL:fix/4822-async-execution-contextvar
Open

fix: propagate ContextVar state to async_execution worker threads#4834
SinzoL wants to merge 1 commit intocrewAIInc:mainfrom
SinzoL:fix/4822-async-execution-contextvar

Conversation

@SinzoL
Copy link
Copy Markdown

@SinzoL SinzoL commented Mar 13, 2026

Task.execute_async() spawns a new threading.Thread for background execution, but threading.Thread does not inherit the parent's contextvars.Context. This causes ContextVar values (e.g. tracing spans, tenant IDs, session state) to be lost in the worker thread. Use contextvars.copy_context() to snapshot the current context and run the worker function inside it via ctx.run(). Fixes #4822


Note

Medium Risk
Touches core async task execution by changing how worker threads are started, which could affect any code relying on thread startup semantics. The change is small but impacts tracing/tenant context propagation across all Task.execute_async calls.

Overview
Fixes lost ContextVar state in async task execution. Task.execute_async() now snapshots the current contextvars context and starts the background thread via ctx.run(...), ensuring context-local values (e.g., tracing/tenant/session data) propagate into the worker thread.

Written by Cursor Bugbot for commit 06973d3. This will update automatically on new commits. Configure here.

Task.execute_async() spawns a new threading.Thread for background
execution, but threading.Thread does not inherit the parent's
contextvars.Context. This causes ContextVar values (e.g. tracing
spans, tenant IDs, session state) to be lost in the worker thread.

Use contextvars.copy_context() to snapshot the current context and
run the worker function inside it via ctx.run().

Fixes crewAIInc#4822
Copy link
Copy Markdown

@alvinttang alvinttang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

This PR fixes ContextVar state not propagating to worker threads spawned by Task.execute_async(). The fix uses contextvars.copy_context() to snapshot the current context and ctx.run() to execute the task function within that snapshot.

Analysis:

  1. The fix is correct and idiomatic: contextvars.copy_context() + ctx.run() is the standard Python pattern for propagating ContextVar state to threads. This is well-documented in the Python stdlib and is the right approach.

  2. ctx.run signature: ctx.run(callable, *args) — here it's ctx.run(self._execute_task_async, agent, context, tools, future). This correctly passes all positional arguments through. Good.

  3. Thread safety of the context copy: copy_context() creates a shallow copy of the current context at the time of the call. Any ContextVar.set() calls in the worker thread will only affect the copy, not the parent. This is the desired behavior — each async task execution gets an isolated copy of the context snapshot. No race conditions here.

  4. Interaction with Future: The future object is still shared between the parent and worker thread (it's passed as an argument, not a ContextVar). This is correct — the Future needs to be the same object so the parent can read the result.

  5. Import placement: import contextvars is added at module top level alongside threading, which is correct. No lazy import needed since contextvars is a stdlib module with negligible import cost.

  6. Broader applicability: Are there other places in crewAI that spawn threads without propagating context? A quick search for threading.Thread across the codebase might reveal similar issues. If so, this pattern should be applied consistently.

  7. No test: Testing ContextVar propagation is straightforward — set a ContextVar in the parent, run execute_async, assert the var is visible in the task function. Would be a valuable regression test.

This is a clean, correct, minimal fix. The only suggestion is to check for other thread-spawning sites that might need the same treatment and to add a test.

@github-actions
Copy link
Copy Markdown
Contributor

This PR is stale because it has been open for 45 days with no activity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] async_execution=True loses ContextVar state — threading.Thread not using copy_context()

2 participants