microsoft/aspire · error · Win32Exception
GetExitCodeProcess failed while reading…
Error message
GetExitCodeProcess failed while reading IsolatedProcess.ExitCode
What it means
After WaitForSingleObject confirms the child exited, ExitCode retrieval calls GetExitCodeProcess to fetch the actual termination code. If that Win32 call returns FALSE, the CLI throws this Win32Exception with the raw error code. Note the kernel can still report the pseudo-code 259 (STILL_ACTIVE) if state is inconsistent, but a hard FALSE from the API indicates a real failure such as an invalid handle.
Solutions
- Check NativeErrorCode — ERROR_INVALID_HANDLE means the process handle was disposed; fix handle/dispose ordering in the caller.
- Ensure ExitCode is read after WaitForExit and before the IsolatedProcess is disposed.
- If exit codes are only needed for diagnostics, wrap the read in try/catch and fall back to logging 'exit code unavailable'.
- Verify the environment (EDR/AV hooks on OpenProcess/GetExitCodeProcess) if handles are provably valid.
- Update the Aspire CLI and report with the exact Win32 error code if reproducible.
Defensive patterns
Strategy: try-catch
Validate before calling
if (!startedProcess.HasExited)
{
await startedProcess.WaitForExitAsync(cancellationToken);
}
// process handle is valid and process exited; ExitCode read should succeed Try / catch
try
{
int code = startedProcess.ExitCode;
}
catch (Win32Exception ex) when (ex.Message.Contains("GetExitCodeProcess failed while reading IsolatedProcess.ExitCode"))
{
// Check ex.NativeErrorCode; ERROR_INVALID_HANDLE means a lifetime/dispose bug
code = -1;
} Prevention
- Ensure the process handle stays alive until the exit code is consumed.
- Read the exit code immediately after WaitForExit, before disposal.
- Avoid storing the process object past its disposal to query exit codes later.
- Check EDR/AV behavior if handles are provably valid yet cross-process queries fail.
When it happens
Trigger: GetExitCode (ExitCodeProvider from StartWindows/StartWindowsSuppressed) passes the wait, then WindowsProcessInterop.GetExitCodeProcess(processHandle, out exitCode) returns false and Marshal.GetLastWin32Error() is surfaced in the exception.
Common situations: Invalid/disposed process handle (same lifetime bugs as the WaitForSingleObject variants), extreme handle-table pressure, or security software blocking cross-process queries.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- WaitForSingleObject failed while reading…
- Failed to create CLI kill-on-parent-exit job object
- Failed to open NUL device
- Failed to open NUL device for stdin
- Failed to set NUL handle inheritance
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/4ff31b5ce965b72f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Cli/Processes/IsolatedProcess.Windows.cs:298
[SupportedOSPlatform("windows")]
private static int GetExitCode(SafeProcessHandle processHandle)
{
ThrowIfDisposed(processHandle, nameof(ExitCode));
// Disambiguate STILL_ACTIVE (259) from a real 259 exit code via a zero-timeout wait.
var waitResult = WindowsProcessInterop.WaitForSingleObject(processHandle, 0);
if (waitResult == WindowsProcessInterop.WaitTimeout)
{
throw new InvalidOperationException("Process has not exited; cannot read ExitCode.");
}
if (waitResult == WindowsProcessInterop.WaitFailed)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "WaitForSingleObject failed while reading IsolatedProcess.ExitCode");
}
if (!WindowsProcessInterop.GetExitCodeProcess(processHandle, out var exitCode))
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "GetExitCodeProcess failed while reading IsolatedProcess.ExitCode");
}
return unchecked((int)exitCode);
}
[SupportedOSPlatform("windows")]
private static Task WaitForProcessHandleExitAsync(SafeProcessHandle processHandle, CancellationToken cancellationToken)
{
ThrowIfDisposed(processHandle, nameof(WaitForExitAsync));
return WindowsProcessInterop.WaitForExitAsync(processHandle, cancellationToken);
}
private static void ThrowIfDisposed(SafeProcessHandle processHandle, string memberName)
{
if (processHandle.IsClosed || processHandle.IsInvalid)
{
throw new InvalidOperationException($"Cannot read {memberName} after the IsolatedProcess has been disposed.");
}View on GitHub (pinned to 25830f84bd)