Skip to content

Commit 44a830f

Browse files
joaomdmouraclaude
andcommitted
fix(skills): carry the caller's context into the worker thread
When resolve_plus_response bridges an async client from inside a running loop it runs the coroutine on a worker thread, which starts with empty ContextVars. A client reading runtime state there — the platform integration token, flow context — would see defaults rather than the caller's values, which is hard to diagnose from the resulting auth or routing failure. Copy the context across, matching how the parallel-summarization bridge in this module already does it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 828259f commit 44a830f

2 files changed

Lines changed: 22 additions & 2 deletions

File tree

lib/crewai/src/crewai/utilities/agent_utils.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,14 @@ async def await_response() -> Any:
108108
loop = None
109109

110110
# asyncio.run() refuses to nest inside a running loop, so hand the coroutine
111-
# to a worker thread with a loop of its own.
111+
# to a worker thread with a loop of its own — carrying a copy of the caller's
112+
# context, since a fresh thread would otherwise start with empty ContextVars
113+
# and a client reading runtime state (the platform token, flow context) would
114+
# see defaults.
112115
if loop and loop.is_running():
116+
ctx = contextvars.copy_context()
113117
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
114-
return pool.submit(asyncio.run, await_response()).result()
118+
return pool.submit(ctx.run, asyncio.run, await_response()).result()
115119

116120
return asyncio.run(await_response())
117121

lib/crewai/tests/utilities/test_agent_utils.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,22 @@ async def main() -> Any:
13071307

13081308
assert asyncio.run(main()) is response
13091309

1310+
def test_carries_context_vars_into_the_worker_thread(self) -> None:
1311+
"""Inside a running loop the coroutine runs on another thread; a client
1312+
reading runtime state (the platform token, flow context) must still see
1313+
the caller's values rather than defaults."""
1314+
from crewai.context import get_platform_integration_token, platform_context
1315+
from crewai.utilities.agent_utils import resolve_plus_response
1316+
1317+
async def call() -> Any:
1318+
return get_platform_integration_token()
1319+
1320+
async def main() -> Any:
1321+
with platform_context("token-from-caller"):
1322+
return resolve_plus_response(call())
1323+
1324+
assert asyncio.run(main()) == "token-from-caller"
1325+
13101326
def test_rejects_an_awaitable_bound_to_a_loop(self) -> None:
13111327
from crewai.utilities.agent_utils import resolve_plus_response
13121328

0 commit comments

Comments
 (0)