From 46be2634c0c2b4759a085d78bb799e8f4edd66be Mon Sep 17 00:00:00 2001 From: JerrettDavis Date: Sat, 18 Apr 2026 10:36:14 -0500 Subject: [PATCH] fix: drain stdout/stderr pipes after WaitForExitAsync to eliminate stream-read race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WaitForExitAsync does not guarantee that redirected stdout/stderr data has been fully delivered to the async reader tasks before it returns — unlike the synchronous WaitForExit() overload. On Linux CI this causes a race where the process exits, WaitForExitAsync unblocks, and the caller reads empty/partial output before the OS has flushed the pipe buffers. Add a call to the no-arg process.WaitForExit() immediately after WaitForExitAsync completes. This is the pattern documented by Microsoft to ensure all pending stream data is drained before consuming the reader task results, stabilising RunCommandAsync_WhenCommandSucceeds_CapturesExitCodeOutputAndError. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/JD.AI/Startup/WelcomeServiceStatusProbe.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/JD.AI/Startup/WelcomeServiceStatusProbe.cs b/src/JD.AI/Startup/WelcomeServiceStatusProbe.cs index f3c3022b..f0cf2791 100644 --- a/src/JD.AI/Startup/WelcomeServiceStatusProbe.cs +++ b/src/JD.AI/Startup/WelcomeServiceStatusProbe.cs @@ -254,6 +254,14 @@ private static Uri BuildHealthUri(Uri baseUri) try { await process.WaitForExitAsync(linkedCts.Token).ConfigureAwait(false); + // WaitForExitAsync does not guarantee that the redirected stdout/stderr + // streams have been fully flushed into the reader tasks before it returns + // (unlike the synchronous WaitForExit() overload). Calling the no-arg + // synchronous overload here ensures all pending stream data is drained + // before we await the reader tasks, eliminating a race on Linux CI where + // the process exits and the async wait completes before the OS has + // delivered all buffered output to the pipe. + process.WaitForExit(); var output = await outputTask.ConfigureAwait(false); var error = await errorTask.ConfigureAwait(false); return (process.ExitCode, output, error, false);