From e38178fd13790acda1c663c5747a4c82e810438c Mon Sep 17 00:00:00 2001 From: 29448 <29448@users.noreply.github.com> Date: Thu, 9 Apr 2026 17:38:38 +0800 Subject: [PATCH] fix(web,mcp): gracefully degrade when MCP loading fails in Web UI worker When an MCP server fails to connect (e.g., due to a port conflict), the Web UI session worker previously crashed entirely, causing messages to get stuck in thinking state indefinitely. Changes: - In `_agent_loop()`, catch `MCPRuntimeError` during `wait_for_background_mcp_loading()` and log a warning instead of letting the exception crash the agent loop. - In `_read_loop()`, when an unexpected exception occurs, clear `_in_flight_prompt_ids` and emit an error status so the frontend can recover instead of leaving prompts stuck in thinking state. Fixes #1766 --- src/kimi_cli/soul/kimisoul.py | 3 +++ src/kimi_cli/web/runner/process.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/kimi_cli/soul/kimisoul.py b/src/kimi_cli/soul/kimisoul.py index 9365c11b9..dee575f1b 100644 --- a/src/kimi_cli/soul/kimisoul.py +++ b/src/kimi_cli/soul/kimisoul.py @@ -64,6 +64,7 @@ from kimi_cli.soul.message import check_message, system, system_reminder, tool_result_to_message from kimi_cli.soul.slash import registry as soul_slash_registry from kimi_cli.soul.toolset import KimiToolset +from kimi_cli.exception import MCPRuntimeError from kimi_cli.tools.dmail import NAME as SendDMail_NAME from kimi_cli.tools.utils import ToolRejectedError from kimi_cli.utils.logging import logger @@ -683,6 +684,8 @@ async def _agent_loop(self) -> TurnOutcome: wire_send(MCPLoadingBegin()) try: await self.wait_for_background_mcp_loading() + except MCPRuntimeError as e: + logger.warning("MCP loading failed, continuing without MCP tools: {}", e) finally: if loading: wire_send(StatusUpdate(mcp_status=self._mcp_status_snapshot())) diff --git a/src/kimi_cli/web/runner/process.py b/src/kimi_cli/web/runner/process.py index 91bfe7eb0..c0f2ff3b3 100644 --- a/src/kimi_cli/web/runner/process.py +++ b/src/kimi_cli/web/runner/process.py @@ -355,6 +355,8 @@ async def _read_loop(self) -> None: raise except Exception as e: logger.warning(f"Unexpected error in read loop: {e.__class__.__name__} {e}") + self._in_flight_prompt_ids.clear() + await self._emit_status("error", reason="read_loop_error", detail=str(e)) async def _handle_out_message(self, message: JSONRPCOutMessage) -> None: """Handle outbound message from worker."""