langchain-ai/deepagents · error · ConcurrentEvalError
task bridge called outside active eval
Error message
task bridge called outside active eval
What it means
The async `task()` host bridge raises ConcurrentEvalError when `self._ptc_state` is None, meaning no eval is currently active on this REPL. PTC state is allocated at eval start and cleared in a finally block, so `task()` is only callable from within a running eval. This detects out-of-band or leaked bridge invocations.
Source
Thrown at libs/partners/quickjs/langchain_quickjs/_repl.py:624
current_loop = asyncio.get_running_loop()
if current_loop is outer_loop:
return await _call()
future = asyncio.run_coroutine_threadsafe(_call(), outer_loop)
try:
return await asyncio.wrap_future(future)
except asyncio.CancelledError:
future.cancel()
raise
def _register_task_bridge(self) -> None:
"""Install the async host function backing top-level `task()`."""
ctx = self._require_ctx()
async def _bridge(raw_input: Any = None) -> Any:
state = self._ptc_state
if state is None:
msg = "task bridge called outside active eval"
raise ConcurrentEvalError(msg)
task_calls = self._task_calls
if task_calls is None:
msg = "task call limiter not initialized"
raise RuntimeError(msg)
payload = _normalize_tool_input(raw_input)
async with task_calls:
try:
result = await self._ainvoke_task_on_outer_loop(
payload,
state=state,
)
except GraphInterrupt:
raise
except Exception as e:
# Subagent dispatches are part of the eval language, not
# PTC calls. Surface their validation/runtime failures as
# eval errors without changing normal `tools.*` semantics.View on GitHub (pinned to a1af029e6e)
Solutions
- Ensure all task() calls complete before the eval's JS finishes (await the promises)
- Use one REPL per concurrent eval — never share a REPL across concurrent evaluations
- Do not schedule deferred task() calls (setTimeout/setInterval) that outlive the eval
Example fix
// before
setTimeout(() => task({description: 'late', subagentType: 'researcher'}), 1000) // fires after eval
// after
await task({ description: 'inline', subagentType: 'researcher' }) Defensive patterns
Strategy: try-catch
Validate before calling
// JS side: only call task() synchronously awaited within the eval
if (typeof task !== 'function') throw new Error('task() unavailable'); Type guard
function taskAvailable() {
return typeof task === 'function';
} Try / catch
try {
const r = await task(payload);
} catch (e) {
if (String(e).includes('outside active eval')) {
// deferred/out-of-band call; do not retry
console.warn('task() called after eval finished');
} else { throw e; }
} Prevention
- Always await task() promises before eval JS returns
- Avoid setTimeout/setInterval that outlive the eval
- One REPL per concurrent evaluation
When it happens
Trigger: The `task` host function is invoked outside an active eval — e.g. a pending JS callback or promise resolved after the eval finished, or the bridge triggered from another thread/concurrent eval context.
Common situations: Fire-and-forget JS scheduling `task()` after the eval returns, concurrent evals sharing one REPL where one eval's bridge call lands after the other's finally-cleaned state, or calling task() from a timer set inside the eval.
Related errors
- task() requires non-empty string field `description`
- task() requires non-empty string field `subagentType`
- task() field `label` must be a string when provided
- task() field `responseSchema` must be an object when provide
- task() requires an active ToolRuntime
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/52060aca820f9c27.
Report an issue: GitHub.