diff --git a/src/kimi_cli/web/runner/process.py b/src/kimi_cli/web/runner/process.py index 16f60a895..ba47a0125 100644 --- a/src/kimi_cli/web/runner/process.py +++ b/src/kimi_cli/web/runner/process.py @@ -661,8 +661,24 @@ async def send_message(self, message: str) -> None: logger.error(f"{e.__class__.__name__} {e}: Invalid JSONRPC in message: {message}") return - process.stdin.write((message + "\n").encode("utf-8")) - await process.stdin.drain() + try: + process.stdin.write((message + "\n").encode("utf-8")) + await process.stdin.drain() + except (BrokenPipeError, ConnectionResetError) as e: + # Subprocess died between our `start()` check above and the actual write. + # `_read_loop` will eventually observe the exit and emit "stopped" / + # "crashed", but right now the caller (FastAPI / websocket handler) would + # otherwise see a raw exception propagate to the response. Emit an error + # status so any attached websocket clients see the failure synchronously. + logger.warning( + f"send_message: subprocess stdin {e.__class__.__name__}; " + f"process likely exited (returncode={process.returncode})" + ) + await self._emit_status( + "error", + reason="stdin_broken", + detail=f"{e.__class__.__name__}: {e}", + ) class KimiCLIRunner: