AI & Agent Dev Bug Sandbox logo
AI & Agent Dev Bug Sandbox
Back to Radar

ShellToolMiddleware.Aafter_agent Blocks The Asyncio Event Loop During Shutdown

ShellToolMiddleware.aafter_agent synchronously executes shutdown commands on the event loop, starving unrelated async tasks; delegate to run_in_executor like abefore_agent.

highConfidence 95%LangchainAffected V1.4.0

Origin Analysis

aafter_agent calls the synchronous after_agent directly instead of using run_in_executor. after_agent waits for subprocess termination with blocking I/O (e.g., subprocess.wait), which blocks the event loop and prevents other asyncio tasks from running.
Run the following Python script:\n```python\nimport asyncio\nimport time\nfrom langchain.agents.middleware import ShellToolMiddleware\n\nasync def measure_timer_delay(started_at: float) -> float:\n await asyncio.sleep(0.05)\n return time.perf_counter() - started_at\n\nasync def main():\n middleware = ShellToolMiddleware(shutdown_commands=("sleep 0.4",))\n runtime = None\n state = {"messages": []}\n updates = await middleware.abefore_agent(state, runtime)\n state.update(updates or {})\n started_at = time.perf_counter()\n timer_task = asyncio.create_task(measure_timer_delay(started_at))\n await middleware.aafter_agent(state, runtime)\n timer_delay = await timer_task\n print(f"Timer resumed after {timer_delay:.3f}s")\n assert timer_delay < 0.2\n\nasyncio.run(main())\n```\nExpected: timer delay < 0.2s. Actual: >0.4s because shutdown command blocks event loop.

Fixing Code Block

Edge Case Audit

run_in_executor uses the default ThreadPoolExecutor; long-running shutdown commands may exhaust threads under high concurrency. Use a dedicated executor with max_workers configured if many agents shut down simultaneously. The sync after_agent must be thread-safe if it mutates shared state. Cancellation of the async wrapper does not kill the subprocess; original synchronous behavior already had this issue. Rollback: revert to the direct call if this causes unexpected context propagation issues, but the current behavior is a known event-loop blocker.

Ecosystem Topology