Skip to content

Discard stdin a command does not read instead of failing on WSManFault 232 (#183) - #185

Merged
bertysentry merged 1 commit into
mainfrom
183-stdin-fed-to-a-command-that-exits-without-reading-it-fails-the-run-with-wsmanfault-232
Sep 28, 2026
Merged

bertysentry merged 1 commit into
mainfrom
183-stdin-fed-to-a-command-that-exits-without-reading-it-fails-the-run-with-wsmanfault-232

Conversation

@bertysentry

Copy link
Copy Markdown
Contributor

Closes #183.

Problem

When a command exits without reading its standard input, or before the input arrives, the server answers the WSMan Send with fault 232, "The pipe is being closed". WsmanClient.RemoteCommand.send(...) turned it into a WinRMFaultException, so execute() and start() failed and lost the command's output and exit code. The CLI failed the same way whenever input was piped into such a command (echo x | … exec hostname, exit 70).

Fix

RemoteCommand.send(...) treats fault 232 like a broken pipe on a local java.lang.Process: it stops sending, discards the rest of the input, and the Receive loop goes on, so the caller gets the command's actual output and exit code. Any other fault still fails.

Every stdin path goes through that method, so the fix covers them all:

  • pre-supplied input (stdin(String|Path|InputStream)) with execute() and start();
  • RemoteProcess.stdin(): flush() and close() stay silent for input the command does not read. A Process pipe would throw an IOException here, but whether the input beats the command's exit varies from host to host (every time on 2008 R2, never in five runs on 2019), and a failure would too. The output and exit code tell the caller how the command went.

The CLI's interactive shell pump also writes through RemoteProcess.stdin(). Input it sends after the remote cmd.exe exited should therefore no longer fail the session. That follows from the code path; I didn't reproduce it live, because the timing window is narrow there.

Tests

Four new FakeWsmanServer tests in CommandStdinTest:

  • execute(), where the first of three read buffers is refused with fault 232: the output and exit code are returned, and nothing more is sent;
  • start() with pre-supplied input, refused the same way;
  • RemoteProcess.stdin(): the refused flush(), a later write and close() don't fail and send nothing more, and the output and exit code (1) are read normally;
  • any other fault on a Send still fails execute() with a WinRMFaultException.

Without the fix, the three fault-232 tests fail with Send failed: HTTP 500 (WSManFault 232): The pipe is being closed. mvn clean verify site passes.

Live check: anaxagore, Windows Server 2008 R2

  • Before: echo x | java -cp target/classes org.metricshub.winrm.cli.WinRmCli -h anaxagore … exec hostname printed winrm-java: Send failed: HTTP 500 (WSManFault 232): The pipe is being closed. and exited 70.
  • After: 3 of 3 runs print ANAXAGORE and exit 0.
  • API probe:
    • stdin("x\n").execute() and stdin("x\n").start() both return ANAXAGORE with exit code 0.
    • A write through RemoteProcess.stdin() 3 s after cmd /c echo early& exit 3 exited doesn't fail, and early and exit code 3 are reported.
    • 1 MB of input to hostname completes in 124 ms.
    • sort still gets its input.

Docs

  • The "Standard input" section of commands.md, and the stdin forwarding paragraph of cli.md: input the command does not read is discarded.
  • Javadoc of CommandRequest.stdin(String) (which the other stdin(...) variants refer to), RemoteProcess.stdin() and CommandCursor.send(...).

Known limit

After the refusal, pre-supplied input from a stream is still read to its end, although nothing more is sent. An endless producer (yes | … exec hostname) therefore runs until the timeout. Stopping the read would require CommandCursor.send(...) to report the refusal, which changes a public interface released since 2.0.00, so it is left out.

🤖 Generated with Claude Code

…t 232 (#183)

A Send answered with WSManFault 232 ("The pipe is being closed") means the
command exited, or closed its stdin, before its input arrived. The client
turned that race into a WinRMFaultException and lost the command's output and
exit code: execute() and start() failed whenever stdin(...) fed a command that
does not read it (every time on Windows Server 2008 R2, now and then on 2016
and 2022), and so did the CLI when input was piped into such a command.

RemoteCommand.send now treats that fault like a broken pipe: it stops sending,
discards the rest of the input, and the Receive loop goes on, so the caller
gets the actual output and exit code. Any other fault still fails. Every stdin
path goes through it: pre-supplied input with execute() and start(), and
RemoteProcess.stdin(), whose flush() and close() stay silent for input the
command does not read, because whether that input beats the command's exit
varies from host to host.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

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

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-27T23:24:32.703682Z 679cef6 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.

@bertysentry
bertysentry merged commit 5a56136 into main Sep 28, 2026
5 checks passed
@bertysentry
bertysentry deleted the 183-stdin-fed-to-a-command-that-exits-without-reading-it-fails-the-run-with-wsmanfault-232 branch September 28, 2026 14:59
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.

stdin fed to a command that exits without reading it fails the run with WSManFault 232

1 participant