{"record":{"id":"f09383e30f118d41","repo":"wavetermdev/waveterm","slug":"procinfo-process32first-w","errorCode":null,"errorMessage":"procinfo: Process32First: %w","messagePattern":"procinfo: Process32First: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/util/procinfo/procinfo_windows.go","lineNumber":59,"sourceCode":"type windowsSnapshot struct {\n\tprocs map[int32]*snapInfo\n}\n\n// MakeGlobalSnapshot enumerates all processes once via CreateToolhelp32Snapshot.\nfunc MakeGlobalSnapshot() (any, error) {\n\tsnap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"procinfo: CreateToolhelp32Snapshot: %w\", err)\n\t}\n\tdefer windows.CloseHandle(snap)\n\n\tprocs := make(map[int32]*snapInfo)\n\n\tvar entry windows.ProcessEntry32\n\tentry.Size = uint32(unsafe.Sizeof(entry))\n\n\tif err := windows.Process32First(snap, &entry); err != nil {\n\t\treturn nil, fmt.Errorf(\"procinfo: Process32First: %w\", err)\n\t}\n\tfor {\n\t\tpid := int32(entry.ProcessID)\n\t\tprocs[pid] = &snapInfo{\n\t\t\tppid:       entry.ParentProcessID,\n\t\t\tnumThreads: entry.Threads,\n\t\t\texeName:    windows.UTF16ToString(entry.ExeFile[:]),\n\t\t}\n\t\tif err := windows.Process32Next(snap, &entry); err != nil {\n\t\t\tif errors.Is(err, windows.ERROR_NO_MORE_FILES) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"procinfo: Process32Next: %w\", err)\n\t\t}\n\t}\n\n\treturn &windowsSnapshot{procs: procs}, nil\n}","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/util/procinfo/procinfo_windows.go#L41-L77","documentation":"After the snapshot is created, Process32First retrieves the first process entry; failure is wrapped in this error. It indicates the snapshot handle exists but its first entry could not be read — an essentially unrecoverable snapshot state.","triggerScenarios":"MakeGlobalSnapshot when windows.Process32First fails on the fresh snapshot handle — almost always caused by an invalid/zeroed ProcessEntry32 (missing Size initialization) or an already-invalidated snapshot handle.","commonSituations":"Copy-pasted ProcessEntry32 declarations that forget entry.Size = uint32(unsafe.Sizeof(entry)); interference from security software invalidating the handle; extremely rare corruption during heavy process churn.","solutions":["Ensure the underlying entry.Size is set to uint32(unsafe.Sizeof(entry)) before the call (library already does this; check vendored forks)","Inspect the wrapped win32 error (errors.Is on windows.ERROR_ACCESS_DENIED etc.)","Retry MakeGlobalSnapshot once — the failure is usually transient or environmental","Update the golang.org/x/sys/windows package if the version has known Process32First bugs"],"exampleFix":"// after (caller retry pattern)\nvar snap any\nvar err error\nfor i := 0; i < 2; i++ {\n\tsnap, err = procinfo.MakeGlobalSnapshot()\n\tif err == nil {\n\t\tbreak\n\t}\n}\nif err != nil {\n\treturn fmt.Errorf(\"process snapshot unavailable: %w\", err)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"snap, err := procinfo.MakeGlobalSnapshot()\nif err != nil {\n\tif strings.Contains(err.Error(), \"Process32First\") {\n\t\tsnap, err = procinfo.MakeGlobalSnapshot() // single retry\n\t}\n\tif err != nil { return err }\n}","preventionTips":["Keep golang.org/x/sys/windows up to date","Never modify the ProcessEntry32 Size initialization in vendored code","Retry snapshot creation on first-entry failure","Report persistent failures with the wrapped win32 error code"],"tags":["windows","syscall","process-enumeration"],"backgroundTag":"toolhelp-snapshot-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}