-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
Summary
When running multiple Runner.run() calls concurrently using asyncio.gather() (common in batch processing scenarios), Python raises a ValueError related to ContextVar token management. This is similar to the issue fixed in #538 for run_streamed(), but affects the regular synchronous Runner.run() pattern.
Environment
- openai-agents version: 0.6.x (also affects forked version with Gemini support)
- Python version: 3.11+
- OS: Linux (production), macOS (development)
Error Message
ValueError: <Token var=<ContextVar name='current_trace' default=None at 0x7f7a7f7279c0> at 0x7f7a73ea0200> was created in a different Context
Stack Trace Location
File "agents/tracing/scope.py", line 49, in reset_current_trace
_current_trace.reset(token)
Reproduction
import asyncio
from agents import Agent, Runner
async def process_batch(inputs: list[str]):
"""Process multiple inputs concurrently - triggers ContextVar error"""
async def process_single(input_text: str):
agent = Agent(
name="processor",
instructions="Process the input",
model="gpt-4.1-mini",
)
result = await Runner.run(agent, input_text)
return result.final_output
# Concurrent execution triggers the error
tasks = [process_single(inp) for inp in inputs]
results = await asyncio.gather(*tasks)
return results
# This will fail with ContextVar error when inputs > 1
asyncio.run(process_batch(["input1", "input2", "input3"]))Root Cause Analysis
The issue is in agents/tracing/traces.py and agents/tracing/scope.py:
-
When
Runner.run()starts, it callstrace.start(mark_as_current=True)which does:self._prev_context_token = Scope.set_current_trace(self)
-
When
Runner.run()finishes, it callstrace.finish(reset_current=True)which does:Scope.reset_current_trace(self._prev_context_token)
-
With concurrent
asyncio.gather()tasks:- Task 1:
set_current_trace()→ gets token A - Task 2:
set_current_trace()→ gets token B - Task 3:
set_current_trace()→ gets token C - Task 1 finishes:
reset_current_trace(token A)→ changes ContextVar - Task 3 finishes:
reset_current_trace(token C)→ ERROR: token C was created when ContextVar had different state
- Task 1:
Relationship to #538
Issue #538 and PR #540 fixed this for run_streamed() by ensuring trace start/finish happen in the same async context. However, the same pattern occurs with concurrent Runner.run() calls, which wasn't addressed.
Current Workaround
We're using RunConfig(tracing_disabled=True) for batch operations:
from agents import RunConfig
batch_config = RunConfig(tracing_disabled=True)
async def process_single(input_text: str):
agent = Agent(...)
result = await Runner.run(agent, input_text, run_config=batch_config)
return result.final_outputThis works but loses all tracing data for batch operations.
Proposed Solutions
- Apply same fix as Start and finish streaming trace in impl metod #540 - Ensure trace lifecycle is managed within each task's context
- Use
contextvars.copy_context()- Give each concurrent task its own context copy - Catch and handle the ValueError - Gracefully handle the reset failure without crashing
Impact
- Frequency: 19 errors in production from a single batch operation
- Affected use case: Any batch processing that uses concurrent
Runner.run()calls - Workaround available: Yes, but loses tracing capability
Additional Context
Production error pattern (all 19 errors within 1 second, same batch):
2025-12-29 17:48:19.219 | generate_content | ContextVar error
2025-12-29 17:48:19.223 | generate_content | ContextVar error
2025-12-29 17:48:19.227 | generate_content | ContextVar error
... (16 more within same second)