Title: BrokenResourceError race condition in stdio_client cleanup when context exits quickly · Issue #1960 · modelcontextprotocol/python-sdk · GitHub
Open Graph Title: BrokenResourceError race condition in stdio_client cleanup when context exits quickly · Issue #1960 · modelcontextprotocol/python-sdk
X Title: BrokenResourceError race condition in stdio_client cleanup when context exits quickly · Issue #1960 · modelcontextprotocol/python-sdk
Description: GitHub Issue: MCP SDK stdio_client Race Condition Repository modelcontextprotocol/python-sdk Title BrokenResourceError race condition in stdio_client cleanup when context exits quickly Body Description When the stdio_client async context...
Open Graph Description: GitHub Issue: MCP SDK stdio_client Race Condition Repository modelcontextprotocol/python-sdk Title BrokenResourceError race condition in stdio_client cleanup when context exits quickly Body Descrip...
X Description: GitHub Issue: MCP SDK stdio_client Race Condition Repository modelcontextprotocol/python-sdk Title BrokenResourceError race condition in stdio_client cleanup when context exits quickly Body Descrip...
Opengraph URL: https://github.com/modelcontextprotocol/python-sdk/issues/1960
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"BrokenResourceError race condition in stdio_client cleanup when context exits quickly","articleBody":"# GitHub Issue: MCP SDK stdio_client Race Condition\n\n## Repository\n`modelcontextprotocol/python-sdk`\n\n## Title\n`BrokenResourceError` race condition in `stdio_client` cleanup when context exits quickly\n\n## Body\n\n### Description\n\nWhen the `stdio_client` async context manager exits quickly (before the subprocess has finished outputting data), a race condition occurs between the `stdout_reader` task and the cleanup code in the `finally` block, resulting in `BrokenResourceError`.\n\n### Reproduction\n\nThis occurs in scenarios where:\n1. A subprocess is spawned via `stdio_client`\n2. The subprocess starts outputting data (e.g., initialization messages)\n3. The calling code exits the context quickly (e.g., user disconnects)\n4. The `finally` block closes `read_stream_writer` while `stdout_reader` is mid-send\n\n### Error\n\n```\nExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)\n +-+---------------- 1 ----------------\n | Traceback (most recent call last):\n | File \"mcp/client/stdio/__init__.py\", line 162, in stdout_reader\n | await read_stream_writer.send(session_message)\n | File \"anyio/streams/memory.py\", line 242, in send\n | self.send_nowait(item)\n | File \"anyio/streams/memory.py\", line 213, in send_nowait\n | raise BrokenResourceError\n | anyio.BrokenResourceError\n +------------------------------------\n```\n\n### Root Cause\n\nIn `mcp/client/stdio/__init__.py`, the `stdio_client` function:\n\n```python\nasync with (\n anyio.create_task_group() as tg,\n process,\n):\n tg.start_soon(stdout_reader)\n tg.start_soon(stdin_writer)\n try:\n yield read_stream, write_stream\n finally:\n # ... process cleanup ...\n await read_stream_writer.aclose() # \u003c-- Closes stream\n await write_stream_reader.aclose()\n```\n\nThe `finally` block closes `read_stream_writer` before the TaskGroup has cancelled its tasks. If `stdout_reader` is in the middle of `await read_stream_writer.send(...)`, it receives `BrokenResourceError`.\n\n### Suggested Fix\n\nCancel the TaskGroup's scope before closing the streams:\n\n```python\nfinally:\n # Cancel background tasks before closing streams\n tg.cancel_scope.cancel()\n \n # ... existing cleanup ...\n await read_stream_writer.aclose()\n await write_stream_reader.aclose()\n```\n\nAlternatively, wrap the stream operations in `stdout_reader` with `BrokenResourceError` handling:\n\n```python\nasync def stdout_reader():\n try:\n async with read_stream_writer:\n # ... existing code ...\n except anyio.BrokenResourceError:\n # Context is closing, exit gracefully\n pass\n```\n\n### Environment\n\n- Python: 3.12.1\n- mcp: (version from pip)\n- anyio: (version from pip)\n- OS: Linux\n\n### Workaround\n\nWe're currently serializing MCP lifecycle operations with an asyncio.Lock to prevent overlapping enter/exit operations, which avoids triggering the race condition.\n","author":{"url":"https://github.com/newsbubbles","@type":"Person","name":"newsbubbles"},"datePublished":"2026-01-26T14:36:56.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":4},"url":"https://github.com/1960/python-sdk/issues/1960"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:1e247f1a-141d-0de2-900f-189c8857c89f |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | B16A:6650E:54496A:77F9CC:6A59E42B |
| html-safe-nonce | ec503a06004e8b7f3f6f0600355d7faec414b1bf8204f2c569491ef4d2627ae2 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJCMTZBOjY2NTBFOjU0NDk2QTo3N0Y5Q0M6NkE1OUU0MkIiLCJ2aXNpdG9yX2lkIjoiNzY5NTcwNDUwODgwMTkzNTc5IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | ae350cb3bfce6a020780d605d101ade167a536eeb93524ef754f34eaa10779e1 |
| hovercard-subject-tag | issue:3856244345 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/modelcontextprotocol/python-sdk/1960/issue_layout |
| twitter:image | https://opengraph.githubassets.com/7d52412a6be7fa74019b3f7000eb2a54e407ca4dec9d8fe0ffa91a37445162db/modelcontextprotocol/python-sdk/issues/1960 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/7d52412a6be7fa74019b3f7000eb2a54e407ca4dec9d8fe0ffa91a37445162db/modelcontextprotocol/python-sdk/issues/1960 |
| og:image:alt | GitHub Issue: MCP SDK stdio_client Race Condition Repository modelcontextprotocol/python-sdk Title BrokenResourceError race condition in stdio_client cleanup when context exits quickly Body Descrip... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | newsbubbles |
| hostname | github.com |
| expected-hostname | github.com |
| None | ba3976babb66479b1c943a8edc0777d96157da48fadc0161f9ddb219deee8353 |
| turbo-cache-control | no-preview |
| go-import | github.com/modelcontextprotocol/python-sdk git https://github.com/modelcontextprotocol/python-sdk.git |
| octolytics-dimension-user_id | 182288589 |
| octolytics-dimension-user_login | modelcontextprotocol |
| octolytics-dimension-repository_id | 862584018 |
| octolytics-dimension-repository_nwo | modelcontextprotocol/python-sdk |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 862584018 |
| octolytics-dimension-repository_network_root_nwo | modelcontextprotocol/python-sdk |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 26f17126414f953984d8ae42f57c0db48e7dbde3 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width