From c785f61c410a69580a050d3a1ece82d107a21511 Mon Sep 17 00:00:00 2001 From: Andrew Farah Date: Tue, 24 Feb 2026 21:55:16 -0800 Subject: [PATCH] Handle BrokenResourceError in stdio client streams --- src/mcp/client/stdio.py | 4 ++-- tests/client/test_stdio.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/mcp/client/stdio.py b/src/mcp/client/stdio.py index 902dc8576..51fc7db2c 100644 --- a/src/mcp/client/stdio.py +++ b/src/mcp/client/stdio.py @@ -158,7 +158,7 @@ async def stdout_reader(): session_message = SessionMessage(message) await read_stream_writer.send(session_message) - except anyio.ClosedResourceError: # pragma: lax no cover + except (anyio.ClosedResourceError, anyio.BrokenResourceError): # pragma: lax no cover await anyio.lowlevel.checkpoint() async def stdin_writer(): @@ -174,7 +174,7 @@ async def stdin_writer(): errors=server.encoding_error_handler, ) ) - except anyio.ClosedResourceError: # pragma: no cover + except (anyio.ClosedResourceError, anyio.BrokenResourceError): # pragma: no cover await anyio.lowlevel.checkpoint() async with anyio.create_task_group() as tg, process: diff --git a/tests/client/test_stdio.py b/tests/client/test_stdio.py index f70c24eee..2439d1825 100644 --- a/tests/client/test_stdio.py +++ b/tests/client/test_stdio.py @@ -86,6 +86,20 @@ async def test_stdio_client_bad_path(): assert "Connection closed" in exc_info.value.error.message +@pytest.mark.anyio +async def test_stdio_client_exits_immediately_surfaces_connection_closed(): + """Regression test for #1564: process exit should not bubble BrokenResourceError.""" + server_params = StdioServerParameters(command=sys.executable, args=["-c", "import sys; sys.exit(0)"]) + + async with stdio_client(server_params) as (read_stream, write_stream): + async with ClientSession(read_stream, write_stream) as session: + with pytest.raises(MCPError) as exc_info: + await session.initialize() + + assert exc_info.value.error.code == CONNECTION_CLOSED + assert "Connection closed" in exc_info.value.error.message + + @pytest.mark.anyio async def test_stdio_client_nonexistent_command(): """Test that stdio_client raises an error for non-existent commands."""