{"record":{"id":"52060aca820f9c27","repo":"langchain-ai/deepagents","slug":"task-bridge-called-outside-active-eval","errorCode":null,"errorMessage":"task bridge called outside active eval","messagePattern":"task bridge called outside active eval","errorType":"exception","errorClass":"ConcurrentEvalError","httpStatus":null,"severity":"error","filePath":"libs/partners/quickjs/langchain_quickjs/_repl.py","lineNumber":624,"sourceCode":"        current_loop = asyncio.get_running_loop()\n        if current_loop is outer_loop:\n            return await _call()\n        future = asyncio.run_coroutine_threadsafe(_call(), outer_loop)\n        try:\n            return await asyncio.wrap_future(future)\n        except asyncio.CancelledError:\n            future.cancel()\n            raise\n\n    def _register_task_bridge(self) -> None:\n        \"\"\"Install the async host function backing top-level `task()`.\"\"\"\n        ctx = self._require_ctx()\n\n        async def _bridge(raw_input: Any = None) -> Any:\n            state = self._ptc_state\n            if state is None:\n                msg = \"task bridge called outside active eval\"\n                raise ConcurrentEvalError(msg)\n            task_calls = self._task_calls\n            if task_calls is None:\n                msg = \"task call limiter not initialized\"\n                raise RuntimeError(msg)\n\n            payload = _normalize_tool_input(raw_input)\n            async with task_calls:\n                try:\n                    result = await self._ainvoke_task_on_outer_loop(\n                        payload,\n                        state=state,\n                    )\n                except GraphInterrupt:\n                    raise\n                except Exception as e:\n                    # Subagent dispatches are part of the eval language, not\n                    # PTC calls. Surface their validation/runtime failures as\n                    # eval errors without changing normal `tools.*` semantics.","sourceCodeStart":606,"sourceCodeEnd":642,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/partners/quickjs/langchain_quickjs/_repl.py#L606-L642","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nsetTimeout(() => task({description: 'late', subagentType: 'researcher'}), 1000) // fires after eval\n// after\nawait task({ description: 'inline', subagentType: 'researcher' })","handlingStrategy":"try-catch","validationCode":"// JS side: only call task() synchronously awaited within the eval\nif (typeof task !== 'function') throw new Error('task() unavailable');","typeGuard":"function taskAvailable() {\n  return typeof task === 'function';\n}","tryCatchPattern":"try {\n  const r = await task(payload);\n} catch (e) {\n  if (String(e).includes('outside active eval')) {\n    // deferred/out-of-band call; do not retry\n    console.warn('task() called after eval finished');\n  } else { throw e; }\n}","preventionTips":["Always await task() promises before eval JS returns","Avoid setTimeout/setInterval that outlive the eval","One REPL per concurrent evaluation"],"tags":["concurrency","quickjs","task-bridge"],"backgroundTag":"eval-lifecycle-violation","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}