From 0e617c32cd580a030910a87fb95a4700dc3be28b Mon Sep 17 00:00:00 2001 From: Lingao Meng Date: Fri, 3 Apr 2026 10:20:45 +0800 Subject: [PATCH] fix(shell): kill subprocess on CancelledError to prevent orphan processes When an agent task is cancelled (e.g. via /stop), the ExecTool was only handling TimeoutError but not CancelledError. This left the child process running as an orphan. Now CancelledError also triggers process.kill() and waitpid cleanup before re-raising. --- nanobot/agent/tools/shell.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/nanobot/agent/tools/shell.py b/nanobot/agent/tools/shell.py index e6e9ac0f5..085d74d1c 100644 --- a/nanobot/agent/tools/shell.py +++ b/nanobot/agent/tools/shell.py @@ -128,6 +128,19 @@ class ExecTool(Tool): except (ProcessLookupError, ChildProcessError) as e: logger.debug("Process already reaped or not found: {}", e) return f"Error: Command timed out after {effective_timeout} seconds" + except asyncio.CancelledError: + process.kill() + try: + await asyncio.wait_for(process.wait(), timeout=5.0) + except asyncio.TimeoutError: + pass + finally: + if sys.platform != "win32": + try: + os.waitpid(process.pid, os.WNOHANG) + except (ProcessLookupError, ChildProcessError) as e: + logger.debug("Process already reaped or not found: {}", e) + raise output_parts = []