diff --git a/astrbot/_internal/protocols/lsp/client.py b/astrbot/_internal/protocols/lsp/client.py index bc9512db9..a600fb901 100644 --- a/astrbot/_internal/protocols/lsp/client.py +++ b/astrbot/_internal/protocols/lsp/client.py @@ -48,11 +48,15 @@ class AstrbotLspClient(BaseAstrbotLspClient): if reader_task is None: return self._reader_task = None + if reader_task is asyncio.current_task(): + return reader_task.cancel() try: await reader_task except asyncio.CancelledError: pass + except Exception: + pass def _handle_reader_task_done(self, task: asyncio.Task[None]) -> None: if self._reader_task is task: diff --git a/tests/unit/test_internal/test_lsp_client.py b/tests/unit/test_internal/test_lsp_client.py index 0381d2257..48299a7f5 100644 --- a/tests/unit/test_internal/test_lsp_client.py +++ b/tests/unit/test_internal/test_lsp_client.py @@ -31,16 +31,27 @@ async def test_lsp_reader_task_failure_marks_client_disconnected_and_logs(): ): await client.connect_to_server(["python", "fake_lsp.py"], "file:///tmp") await asyncio.sleep(0) - - reader_task = client._reader_task - assert reader_task is not None - _ = reader_task.exception() await asyncio.sleep(0) assert client.connected is False mock_log.error.assert_called_once() +@pytest.mark.asyncio +async def test_lsp_stop_reader_task_swallows_failed_reader_exceptions(): + """Test reader teardown does not re-raise prior reader failures.""" + client = AstrbotLspClient() + + async def fail_reader() -> None: + raise RuntimeError("reader crashed") + + client._reader_task = asyncio.create_task(fail_reader()) + await asyncio.sleep(0) + + await client._stop_reader_task() + assert client._reader_task is None + + @pytest.mark.asyncio async def test_lsp_connect_to_server_cancels_previous_reader_task_before_restart(): """Test reconnect tears down an existing reader task before replacing it."""