{"record":{"id":"4ff31b5ce965b72f","repo":"microsoft/aspire","slug":"getexitcodeprocess-failed-while-reading-isolatedprocess","errorCode":null,"errorMessage":"GetExitCodeProcess failed while reading IsolatedProcess.ExitCode","messagePattern":"GetExitCodeProcess failed while reading IsolatedProcess\\.ExitCode","errorType":"exception","errorClass":"Win32Exception","httpStatus":null,"severity":"error","filePath":"src/Aspire.Cli/Processes/IsolatedProcess.Windows.cs","lineNumber":298,"sourceCode":"    [SupportedOSPlatform(\"windows\")]\n    private static int GetExitCode(SafeProcessHandle processHandle)\n    {\n        ThrowIfDisposed(processHandle, nameof(ExitCode));\n\n        // Disambiguate STILL_ACTIVE (259) from a real 259 exit code via a zero-timeout wait.\n        var waitResult = WindowsProcessInterop.WaitForSingleObject(processHandle, 0);\n        if (waitResult == WindowsProcessInterop.WaitTimeout)\n        {\n            throw new InvalidOperationException(\"Process has not exited; cannot read ExitCode.\");\n        }\n        if (waitResult == WindowsProcessInterop.WaitFailed)\n        {\n            throw new Win32Exception(Marshal.GetLastWin32Error(), \"WaitForSingleObject failed while reading IsolatedProcess.ExitCode\");\n        }\n\n        if (!WindowsProcessInterop.GetExitCodeProcess(processHandle, out var exitCode))\n        {\n            throw new Win32Exception(Marshal.GetLastWin32Error(), \"GetExitCodeProcess failed while reading IsolatedProcess.ExitCode\");\n        }\n\n        return unchecked((int)exitCode);\n    }\n\n    [SupportedOSPlatform(\"windows\")]\n    private static Task WaitForProcessHandleExitAsync(SafeProcessHandle processHandle, CancellationToken cancellationToken)\n    {\n        ThrowIfDisposed(processHandle, nameof(WaitForExitAsync));\n        return WindowsProcessInterop.WaitForExitAsync(processHandle, cancellationToken);\n    }\n\n    private static void ThrowIfDisposed(SafeProcessHandle processHandle, string memberName)\n    {\n        if (processHandle.IsClosed || processHandle.IsInvalid)\n        {\n            throw new InvalidOperationException($\"Cannot read {memberName} after the IsolatedProcess has been disposed.\");\n        }","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Cli/Processes/IsolatedProcess.Windows.cs#L280-L316","documentation":"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.","triggerScenarios":"GetExitCode (ExitCodeProvider from StartWindows/StartWindowsSuppressed) passes the wait, then WindowsProcessInterop.GetExitCodeProcess(processHandle, out exitCode) returns false and Marshal.GetLastWin32Error() is surfaced in the exception.","commonSituations":"Invalid/disposed process handle (same lifetime bugs as the WaitForSingleObject variants), extreme handle-table pressure, or security software blocking cross-process queries.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (!startedProcess.HasExited)\n{\n    await startedProcess.WaitForExitAsync(cancellationToken);\n}\n// process handle is valid and process exited; ExitCode read should succeed","typeGuard":null,"tryCatchPattern":"try\n{\n    int code = startedProcess.ExitCode;\n}\ncatch (Win32Exception ex) when (ex.Message.Contains(\"GetExitCodeProcess failed while reading IsolatedProcess.ExitCode\"))\n{\n    // Check ex.NativeErrorCode; ERROR_INVALID_HANDLE means a lifetime/dispose bug\n    code = -1;\n}","preventionTips":["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."],"tags":["windows","process","win32","exit-code"],"backgroundTag":"invalid-state-transition","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T09:17:21.228Z"}