Read each output stream to end of stream, not just until exit - #87
Merged
Merged
Conversation
AsyncProcessStreamReader stopped reading once the process exited, plus one final read per stream. A process can exit with tens of kilobytes still in the pipe, and everything past that final read was silently dropped. Read each stream until it reports end of stream instead, keeping the race against cancellation so a descendant holding the pipe open still cannot hang a cancelled call. Fixes #84 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGpRxGxuvhstHo5zXQoWSA
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGpRxGxuvhstHo5zXQoWSA
|
This was referenced Sep 26, 2026
This was referenced Sep 27, 2026
This was referenced Sep 28, 2026
This was referenced Sep 28, 2026
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.



Fixes #84
What changed
AsyncProcessStreamReader.Startused to loopwhile (!process.HasExited), finish the reads already in progress, and then make one more read of at most 4096 chars per stream. A process can exit with up to 64 KB per pipe still unread on Linux, so a large amount of output could be dropped. The exit code was still 0 and no error was raised.Startnow runs one read-to-EOF loop per stream (ReadToEnd), and the two loops run concurrently. Both are still raced against the caller's cancellation through the existingDrainOrAbandon/Abandonpath, so a descendant that holds the pipe open still cannot hang a cancelled call. A failed read, such as a decode error, still reaches the caller, becauseDrainOrAbandonawaits both reads. That also removes the need for the oldIsFaultedspecial case.ReadAndCallback/ReadCallbackare folded intoReadToEnd, and the BOM stripping is unchanged.CLAUDE.md's "Stream reading" section used to say that a final read after exit is enough to keep short-lived processes from losing output. It now describes the read-to-EOF behaviour.
Tests
ExecuteAsyncShouldDeliverOutputStillInThePipeWhenTheProcessExitswrites 200,000 bytes through the existingGetEmitFileBytesCommandemitter (caton Unix, PowerShell on Windows) and checks that every character arrives. It repeats this 10 times, because the loss depends on timing.This PR is independent of #86, which fixes #85 in
LineOutputHandler. Each one branches frommain.🤖 Generated with Claude Code
https://claude.ai/code/session_01CGpRxGxuvhstHo5zXQoWSA
Generated by Claude Code