From 86f6558707aa395e95bdf401bb416624adf78ec7 Mon Sep 17 00:00:00 2001 From: Brian Noah Date: Tue, 7 Jul 2026 17:53:31 -0500 Subject: [PATCH] fix: catch asyncio.CancelledError in close_mcp shutdown When an MCP server (e.g. stdio browser-agent subprocess) does not terminate within the AsyncExitStack.aclose() timeout, asyncio raises CancelledError. The existing exception handler only caught RuntimeError and BaseExceptionGroup, so CancelledError escaped the except block and crashed nanobot with exit code 1 on every shutdown. Add asyncio.CancelledError to the caught exception tuple so the error is logged at debug level and shutdown completes cleanly. Stack trace from the crash: Traceback (most recent call last): File ".../nanobot/agent/loop.py", line 1194, in close_mcp await stack.aclose() asyncio.exceptions.CancelledError During handling of the above exception, another exception occurred: Traceback (most recent call last): File ".../asyncio/__main__.py", line ?, in File ".../nanobot/agent/loop.py", line ?, in close_mcp ... RuntimeError: ... (or BaseExceptionGroup) not caught SystemExit: 1 --- nanobot/agent/tools/mcp.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/nanobot/agent/tools/mcp.py b/nanobot/agent/tools/mcp.py index 7919b9ad..2d17be59 100644 --- a/nanobot/agent/tools/mcp.py +++ b/nanobot/agent/tools/mcp.py @@ -1410,6 +1410,10 @@ async def _close_server(state: Any, server_name: str) -> None: return try: await stack.aclose() + except asyncio.CancelledError: + if asyncio.current_task().cancelling() > 0: + raise + logger.debug("MCP server '{}' cleanup error (can be ignored)", server_name) except (RuntimeError, BaseExceptionGroup): logger.debug("MCP server '{}' cleanup error (can be ignored)", server_name) @@ -1423,5 +1427,9 @@ async def close_mcp_servers(state: Any) -> None: for name, connection in connections: try: await connection.aclose() + except asyncio.CancelledError: + if asyncio.current_task().cancelling() > 0: + raise + logger.debug("MCP server '{}' cleanup error (can be ignored)", name) except (RuntimeError, BaseExceptionGroup): logger.debug("MCP server '{}' cleanup error (can be ignored)", name)