Repository navigation
Windows: MCP servers with heavy stderr output fail with "Transport closed" error #7155
Description
What version of Codex is running?
codex-cli 0.63.0
What subscription do you have?
Pro
Which model were you using?
gpt-5.1-codex-max
What platform is your computer?
Microsoft Windows NT 10.0.26200.0 x64
What issue are you seeing?
MCP servers that produce heavy stderr output during tool execution fail with "Transport closed" errors on Windows. This affects servers like bc-code-intelligence-mcp but not servers with minimal stderr like @modelcontextprotocol/server-filesystem.
Error Message:
[WARN] MCP tool call error: "tool call error: tool call failed for `bc-code-intel/set_workspace_info`
Caused by: tools/call failed: Transport closed"
Key Evidence:
- bc-code-intel works perfectly when tested manually via stdio
- bc-code-intel works in Claude Code, GitHub Copilot, and Cursor
- filesystem MCP works perfectly in Codex (minimal stderr)
- bc-code-intel ONLY fails in Codex on Windows with heavy stderr (189-237ms of output)
What steps can reproduce the bug?
- Configure bc-code-intelligence-mcp in
config.toml:
[mcp_servers.bc-code-intel]
command = "node"
args = ["C:\\path\\to\\bc-code-intelligence-mcp\\dist\\index.js"]
startup_timeout_sec = 60- Start Codex and call
set_workspace_infotool - Server processes successfully (logs show completion: "📊 Total: 123 topics, 16 specialists")
- Error occurs ~7ms after server completes: "Transport closed"
Manual Test (WORKS):
echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"set_workspace_info","arguments":{"workspace_root":"C:/test","available_mcps":["bc-code-intel"]}}}' | node dist/index.js
# Returns valid JSON-RPC response successfullyWhat is the expected behavior?
MCP servers should work regardless of stderr output volume. The server should complete tool execution and return results without "Transport closed" errors.
Root Cause Analysis
Location: codex-rs/rmcp-client/src/rmcp_client.rs lines 133-149
The Bug: The stderr reading task blocks indefinitely on lines().next_line().await without timeout handling. When MCP servers produce heavy stderr output (like bc-code-intel's 189-237ms of loading logs), this interferes with the stdio transport on Windows.
Code:
if let Some(stderr) = stderr {
tokio::spawn(async move {
let mut reader = BufReader::new(stderr).lines();
loop {
match reader.next_line().await { // ← NO TIMEOUT
Ok(Some(line)) => {
info!("MCP server stderr ({program_name}): {line}");
}
Ok(None) => break,
Err(error) => {
warn!("Failed to read MCP server stderr ({program_name}): {error}");
break;
}
}
}
});
}Smoking Gun: This exact pattern was already fixed in commit 73ed30d7e ("Avoid hang when tool's process spawns grandchild that shares stderr/stdout") for shell tool execution in codex-rs/core/src/exec.rs lines 584-625, but was never applied to MCP client code.
Proposed Fix (UNTESTED)
Note: This fix is based on code analysis and the existing pattern in exec.rs, but has NOT been tested locally due to build environment requirements. The Codex team should validate this approach.
Apply the same timeout pattern from exec.rs to prevent stderr blocking:
if let Some(stderr) = stderr {
tokio::spawn(async move {
use tokio::time::{timeout, Duration};
let mut reader = BufReader::new(stderr).lines();
loop {
match timeout(Duration::from_secs(5), reader.next_line()).await {
Ok(Ok(Some(line))) => {
info!("MCP server stderr ({program_name}): {line}");
}
Ok(Ok(None)) => break,
Ok(Err(error)) => {
warn!("Failed to read MCP server stderr ({program_name}): {error}");
break;
}
Err(_) => {
warn!("MCP server stderr ({program_name}): read timeout after 5s, continuing...");
}
}
}
});
}Additional Information
Testing Done:
- ✅ Verified bc-code-intel works with manual stdio testing
- ✅ Tested with SDK 0.4.0 and 1.22.0 - same issue
- ✅ Confirmed filesystem MCP works in Codex (low stderr)
- ✅ Isolated issue to Codex's stderr handling, not bc-code-intel
- ❌ Proposed fix NOT tested locally (build environment setup incomplete)
Files Involved:
codex-rs/rmcp-client/src/rmcp_client.rs- needs fixcodex-rs/core/src/exec.rs- reference implementation
Related Commits:
73ed30d7e- Fixed similar issue for shell toolsf828cd289- Windows MCP server execution fix
This is a Windows-specific race condition in MCP client stderr handling that should be fixed to support MCP servers with verbose diagnostic output.
Activity
AyinMostima commentedon Dec 23, 2025
I've also encountered this problem. Codex is truly disappointing, a bunch of idiots!
vincentchau0709-lang commentedon Jan 15, 2026
Hopefully Codex will fix this problem ASAP, this is truely disappointing.
gitown commentedon Feb 7, 2026
The problem still exist in Codex VS Extension v0.4.71 and Codex CLI v0.98.0
cc @gpeal
sstklen commentedon Feb 26, 2026
alario-tang commentedon Apr 22, 2026
Related, but I do not think this is a duplicate of #7155.
I just filed #18977 for a different
Transport closedfailure mode on macOS:stdin_end/stdin_close/transport_closeSo the overlap is the user-visible symptom (
Transport closed), but the suspected trigger is different:Transport closedeven when the underlying operation is healthy #18977: macOS + MCP host / stdio transport lifecycle robustness pathLinking the two in case the maintainers see a shared host-side weakness around MCP process / transport management:
#18977
HaoYue1027 commentedon Oct 13, 2026
I isolated a stderr-encoding failure that may explain a subset of Windows MCP transport failures reported here. It reproduces with two tiny diagnostic writes; high stderr volume is not required. I have not established that it explains the original report.
Environment and observed failure
0.162.0-alpha.17.2.0.23.2, FastMCP4.0.0b1, MCP Python SDK2.3.0, Python3.12.13, uv0.12.9.uvx --prerelease=allow basic-memory mcp.Basic Memory's FastMCP startup banner runs before the stdio protocol loop. The client logs
stream did not contain valid UTF-8, then the server fails while flushing subsequent banner output:The MCP initialization then fails with
connection closed: initialize response, and no tools are registered.BrokenPipeErrorwas not observed on this Windows machine.Controlled isolation
The dependency-free Python MCP fixture below can be saved as
stderr-pipe-fixture.py. It first writesb"invalid UTF-8 byte: \xff\n"to stderr, waits 350 ms, and then writes ASCII diagnostics. It never accesses knowledge files or account data; it writes a small result JSON beside itself.Register it with an isolated MCP configuration, substituting absolute paths to Python and the script below:
Start Codex app-server and query
mcpServerStatus/list. Repeat in a fresh process with the last argument changed tosurvive.exitsurviveThis separates stderr reader lifetime from directly terminating the server on decode failure. The stderr read end closes; a later unhandled server write error causes the process to exit.
For the real Basic Memory server, setting only service-level
PYTHONIOENCODING=utf-8changes standard stream encoding while leavingsys.flags.utf8_mode == 0. The exact command above then initializes and exposes 21 tools. A real read-only note call also succeeded. This acceptance used no--offlineargument or auto-update environment override. Disabling only the banner also worked with GBK stderr, identifying the banner as the current trigger.Code path and suggested regression
The current main source uses
BufReader.lines()/next_line()for stderr and breaks the reader loop on decoding error:https://github.com/openai/codex/blob/main/codex-rs/rmcp-client/src/stdio_server_launcher.rs
Main is explanatory source evidence, not a claim about the tested binary's exact commit. The reader-close effect was independently observed with the fixture above.
Would you consider retaining and draining the diagnostic pipe after malformed UTF-8, using bounded byte reads and lossy display? Stdout MCP JSON should remain strictly validated. A regression can write an invalid byte to stderr followed by ASCII stderr and then a valid initialize response; initialization should survive and the second write should not fail because the client dropped the reader.
The MCP specification permits UTF-8 strings on stderr. This report concerns compatibility and robustness when a server emits malformed diagnostic bytes; it does not allege a normative protocol violation by Codex:
https://modelcontextprotocol.io/specification/2025-11-25/basic/transports
Related work and limits
FastMCP #5641 reports a similar Windows GBK/filtered-environment problem. Its linked PR #5642 changes the FastMCP client, so it does not directly fix Codex's reader:
PrefectHQ/fastmcp#5641
PrefectHQ/fastmcp#5642
Checked on 2026-10-13: #7155 remains open and the main reader still has the break-on-error branch. The latest stable release found was
rust-v0.162.1; that binary was not tested. Please confirm whether this belongs here or should be split into a focused stderr-decoding issue. Expected: malformed diagnostics do not tear down an otherwise valid stdio MCP connection. Actual: the reader closes and a subsequent server diagnostic write can abort initialization.Complete minimal stderr fixture (Python standard library only)