{"record":{"id":"ccdabf7b017aee80","repo":"tailscale/tailscale","slug":"waitforsingleobject-w","errorCode":null,"errorMessage":"WaitForSingleObject: %w","messagePattern":"WaitForSingleObject: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"util/winutil/restartmgr_windows.go","lineNumber":758,"sourceCode":"// cmdLineInfo and will reside inside the session identified by sessID, with the\n// security token whose logon is associated with sessID. The child process's\n// environment will be inherited from the session token's environment.\nfunc RunProcessInSession(sessID SessionID, cmdLineInfo CommandLineInfo, timeout time.Duration) (uint32, error) {\n\ttimeoutMillis, err := wingoes.DurationToTimeoutMilliseconds(timeout)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\n\tpi, err := startProcessInSessionInternal(sessID, cmdLineInfo, 0)\n\tif err != nil {\n\t\treturn 1, err\n\t}\n\twindows.CloseHandle(pi.Thread)\n\tdefer windows.CloseHandle(pi.Process)\n\n\twaitCode, err := windows.WaitForSingleObject(pi.Process, timeoutMillis)\n\tif err != nil {\n\t\treturn 1, fmt.Errorf(\"WaitForSingleObject: %w\", err)\n\t}\n\tif e := windows.Errno(waitCode); e == windows.WAIT_TIMEOUT {\n\t\treturn 1, e\n\t}\n\tif waitCode != windows.WAIT_OBJECT_0 {\n\t\t// This should not be possible; log\n\t\treturn 1, fmt.Errorf(\"unexpected state from WaitForSingleObject: %d\", waitCode)\n\t}\n\n\tvar exitCode uint32\n\tif err := windows.GetExitCodeProcess(pi.Process, &exitCode); err != nil {\n\t\treturn 1, err\n\t}\n\treturn exitCode, nil\n}\n\nfunc startProcessInSessionInternal(sessID SessionID, cmdLineInfo CommandLineInfo, extraFlags uint32) (*windows.ProcessInformation, error) {\n\tif err := cmdLineInfo.Validate(); err != nil {","sourceCodeStart":740,"sourceCodeEnd":776,"githubUrl":"https://github.com/tailscale/tailscale/blob/6e0912f97994f927632b34ae9e63b53d6516a6ac/util/winutil/restartmgr_windows.go#L740-L776","documentation":"RunProcessInSession creates a child in the target session then blocks in WaitForSingleObject(pi.Process, timeoutMillis). This is the raw syscall failure branch (err != nil), distinct from wait results like WAIT_TIMEOUT which are handled separately. With a freshly created, still-open process handle this is nearly impossible and signals handle misuse.","triggerScenarios":"pi.Process closed concurrently before the wait (the deferred CloseHandle firing early via a wrapper); an invalid timeout value after conversion; memory corruption of the handle variable.","commonSituations":"Wrapping RunProcessInSession with custom context/timeout logic that closes handles; copying ProcessInformation structs after partial close.","solutions":["Ensure nothing closes the process handle before RunProcessInSession returns","Validate the timeout: negative or overflow-large durations are converted by wingoes.DurationToTimeoutMilliseconds - log the millis value","Treat recurrence as an integrity bug: audit all CloseHandle paths for the child process"],"exampleFix":"// before\npi, _ := startProcessInSession(sessID, cli)\ngo func() { pi.Process.Close() }() // races with internal wait\n\n// after\n// let RunProcessInSession own the full lifecycle; do not close pi externally\nexitCode, err := winutil.RunProcessInSession(sessID, cli, 30*time.Second)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"exitCode, err := winutil.RunProcessInSession(sessID, cli, timeout)\nif err != nil {\n    if errors.Is(err, windows.ERROR_INVALID_HANDLE) {\n        // handle lifecycle bug in caller; do not retry blindly\n        return fmt.Errorf(\"process handle invalidated during wait: %w\", err)\n    }\n    return err\n}","preventionTips":["Let RunProcessInSession own the child handle lifecycle end to end","Never close pi.Process/pi.Thread externally around the wait","Validate timeout values before passing them in (avoid negative durations)"],"tags":["go","windows","waitforsingleobject","process","handle"],"backgroundTag":"waitforsingleobject-failed","analyzedSha":"6e0912f97994f927632b34ae9e63b53d6516a6ac","analyzedAt":"2026-08-18T08:17:25.280Z","contentChangedAt":"2026-08-18T08:17:25.280Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}