Repository navigation
Conversation
There was a problem hiding this comment.
🔵 Needs a closer look
No blocking code issues were identified, but completion of the required Linux and Windows CI checks remains unverified.
No open issues 🎉
What changed in this PR
Addresses #472 by stabilizing exact stderr assertions in MCP stdio tests that force Windows relays onto POSIX. Production code is unchanged.
Changes:
- Adds a helper that removes whole lines matching either known asyncio child-watcher race warning.
- Tests that both warning forms are removed while unrelated diagnostics remain intact.
- Applies the helper to three exact stderr checks.
| File | Description |
|---|---|
tests/mcp/test_stdio.py |
Adds targeted warning filtering, regression coverage, and updated stderr assertions. |
Review effort: Balanced
test_stdout_noise_is_diverted_and_wire_is_restored[True] failed on 3.12 because the stdio server's stderr carried "child process pid 3098 exit status already read: will report returncode 255" (#472). relay=True forces the Windows-only relays onto POSIX. When the stdin relay exits on its own as the test closes stdin, and _relay_stdin's cleanup runs before the event loop has processed that exit, process.kill() goes through Popen.send_signal, which polls first (bpo-38630). That waitpid reaps the relay, and asyncio's child watcher then finds it gone, reports 255 and logs the warning. On Windows, where the relays run in production, nothing reaps. The three forced-relay tests that compare the server's stderr exactly now read it through _server_stderr, which drops whole lines matching either watcher's warning (pidfd: "exit status already read", threaded: "Unknown child process pid") and keeps everything else. Two tests pin both directions: both warnings are dropped with the surrounding lines intact, and a line that only contains one is kept. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The race is rare on its own, so the first fix only covered the three exact stderr checks it was seen against. Under four xdist workers on korvid-runners it hit a fourth on 3.12, twice: test_stdio_delivers_large_utf8_output_without_truncation[True] expected an empty stderr log and got "child process pid ... exit status already read: will report returncode 255". The forced-relay entrypoint now logs that exact asyncio warning at startup, so every relay=True test that reads the server's stderr has to filter it on every run instead of passing on a lucky one. With the line emitted, exactly that test failed; it now reads its stderr log through _server_stderr, which accepts text as well as bytes, and test_stdio.py passes in full. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
hellices
force-pushed
the
fix/472-relay-watcher-stderr
branch
from
October 23, 2026 17:24
c8c7e52 to
0d5d2b5
Compare
5 tasks done
Owner
Author
|
Superseded by #482, which carries this pull request's commits unchanged (0b1a74f and 96a56bd) together with the other open fixes and the dependency security bumps, so all of them need one CI run and one merge instead of a rebase after each merge. Closing in favour of #482; the branch stays, so this can be reopened if needed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #472
Summary
test_stdout_noise_is_diverted_and_wire_is_restored[True]failed ontest (3.12)in #469's CI because the stdio server's stderr carried one extra line:child process pid 3098 exit status already read: will report returncode 255.That line is asyncio's child watcher inside the server, racing
Popen:relay=Trueforces the Windows-only relays onto POSIX._relay_stdin's cleanup runs before the event loop has processed that exit,process.returncodeis stillNoneand the cleanup callsprocess.kill().Popen.send_signalpolls the child first (bpo-38630), and on POSIX thatwaitpidreaps the relay.On Windows, where the relays run in production, nothing reaps, so production never writes this line.
_server_stderr. It drops whole lines matching either watcher's warning and keeps everything else. The pidfd watcher writes "exit status already read"; the threaded watcher writes "Unknown child process pid". The product code is unchanged, because the race only exists where the tests run the relays on POSIX.Update after #471's CI. Under four xdist workers on
korvid-runners, the race hit a fourth exact check on 3.12, twice:test_stdio_delivers_large_utf8_output_without_truncation[True]expected an empty stderr log. Rather than wait for the next one, the forced-relay entrypoint now logs asyncio's exact warning at startup. Everyrelay=Truetest that reads the server's stderr therefore has to filter it on every run, not just on a lucky one. With the line emitted, exactly that test failed. It now reads its log through_server_stderr, which accepts text as well as bytes, andtests/mcp/test_stdio.pypasses in full (57 passed, twice). The branch is rebased ontomainat 9ab38db, and the full suite passes there:pytest -n 8 -q, 13,752 passed, 25 skipped. The CI table below is from the first push.Verification
test_server_stderr_drops_the_relay_child_watcher_race(both watcher forms) failed first against a helper that only decoded, and passes now.test_server_stderr_keeps_every_other_linepins the other direction: a line that only contains one of the warnings, or a shortened form, is kept.tests/mcp/test_stdio.pypasses in full (57 passed).Run locally on macOS with Python 3.13 and the frozen lock. Each step ran directly with the project's environment rather than through
make, because this machine cannot sync from PyPI:pytest -p no:tach tests/mcp/test_stdio.pypython scripts/check_source_size.pyruff check src/ tests/andruff format --check src/ tests/SKIP=mypy,source-size pre-commit run --files tests/mcp/test_stdio.pymypytach checkpytest -n 8 -qThis PR's CI (run 39938500526, hosted runners) passed every required check. That includes the forced-relay stdio tests on Linux with 3.11, 3.12 and 3.13, and
windows-test, where the relays run natively:test (3.11)test (3.12)test (3.13)windows-testChecklist
make checkpasses locally.and a fail-closed audit entry, and sensitive reads are still masked.