Skip to content

test: tolerate the stdio transport's close race on teardown - #9

Merged
Lexus2016 merged 1 commit into
mainfrom
test/windows-stdio-teardown-1109
Nov 9, 2026
Merged

Lexus2016 merged 1 commit into
mainfrom
test/windows-stdio-teardown-1109

Conversation

@Lexus2016

Copy link
Copy Markdown
Owner

What fails

ci run 42595057330 on 0c573a69 is red only on test (windows-latest, 3.11) (ubuntu 3.11/3.13 and lint pass). The failure is at teardown, after every assertion:

FAILED tests/test_project_catalog.py::test_stdio_binding_selects_only_current_client_context - BrokenResourceError()
  tests/test_project_catalog.py:376 in probe
    async with stdio_client(params) as (read, write), ClientSession(...)
  mcp/client/stdio/__init__.py:162 in stdout_reader
    await read_stream_writer.send(session_message)
  anyio/streams/memory.py:220 in send_nowait
    raise BrokenResourceError

Why

mcp 1.26.0's stdio_client.stdout_reader catches anyio.ClosedResourceError only. When the child writes its last line while the client is closing the read stream, the send raises BrokenResourceError instead, and the transport's TaskGroup raises that group into the caller — masking a clean shutdown. The Windows lane loses the race because process exit and pipe teardown are slower there. This is not a server bug: the child logged only its three startup lines and no traceback, and every assertion had already passed.

Change

  • _is_benign_stream_close / _run_stdio_probe in tests/test_project_catalog.py, mirroring server._reraise_unless_stdio_disconnect: a group made only of BrokenResourceError / ClosedResourceError leaves is tolerated, any other leaf (real error, cancellation, KeyboardInterrupt, SystemExit) is re-raised.
  • The probe is split into first_client / second_client so the second session — selection must not leak between clients — still runs on Windows after the first session's teardown.
  • New test_stdio_probe_teardown_close_is_tolerated_but_real_errors_are_not pins the filter: a BrokenResourceError-only group is swallowed, a group that also carries ValueError is re-raised.

Evidence

Local, Linux, Python 3.11.2, uv sync --frozen + embedded model:

  • pytest -q tests/test_project_catalog.py — 15 passed (14 before, +1 new).
  • test_stdio_binding_selects_only_current_client_context — passed 3/3 separate runs.
  • Mutation: with _is_benign_stream_close forced to False, the new test fails with the BrokenResourceError group; restored, it passes.
  • ruff check tests/test_project_catalog.py clean; mypy tests/test_project_catalog.py reports the same 4 pre-existing union-attr findings on stale.content[0].text, none new.

The Windows lane in this PR's own ci run is the proof for the failure that could not be reproduced on Linux.

The new stdio probe fails on the Windows lane (run 42595057330, job
test (windows-latest, 3.11)) with BrokenResourceError raised out of
stdio_client's TaskGroup. mcp 1.26's stdout_reader catches
ClosedResourceError only: when the child writes its last line while the
client closes the read stream, the send raises BrokenResourceError and
the group surfaces through the caller, masking a clean shutdown.

All assertions run inside a live session, so only that closed-stream
leaf is treated as benign, mirroring server._reraise_unless_stdio_disconnect;
any real error, cancellation, KeyboardInterrupt or SystemExit still
fails the test. The probe is split into two sessions so the second
one (selection must not leak between clients) still runs after the
first one's teardown.

New test pins the filter: a group of only BrokenResourceError is
tolerated and a mixed group is re-raised.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-09T12:12:46.949776Z e312c0b PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e312c0ba3a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +370 to +374
if isinstance(exc, BaseExceptionGroup):
_, remaining = exc.split(_is_benign_stream_close)
if remaining is not None:
raise
return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require probe completion before tolerating stream errors

If a stream closes during session initialization or a tool request, this wrapper also swallows the resulting BrokenResourceError/ClosedResourceError, even though the remaining assertions never execute. The exception type alone does not establish that the failure happened during teardown: the try covers the entire probe, and neither client records that its assertions completed. Consequently, a transport regression can make the integration test pass without verifying project selection or client isolation. Record successful completion inside each session after its final assertion, and tolerate these exceptions only when that marker is set.

Useful? React with 👍 / 👎.

@Lexus2016
Lexus2016 merged commit b83484b into main Nov 9, 2026
5 checks passed
@Lexus2016
Lexus2016 deleted the test/windows-stdio-teardown-1109 branch November 9, 2026 12:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant