wavetermdev/waveterm · error

procinfo: CreateToolhelp32Snapshot: %w

Error message

procinfo: CreateToolhelp32Snapshot: %w

What it means

MakeGlobalSnapshot opens a Win32 toolhelp snapshot of all processes via windows.CreateToolhelp32Snapshot; when the syscall fails this error wraps the underlying windows error. Without a snapshot no process data (ppid, threads, exe name) can be enumerated.

Source

Thrown at pkg/util/procinfo/procinfo_windows.go:49

}

// snapInfo holds the data collected in a single pass of CreateToolhelp32Snapshot.
type snapInfo struct {
	ppid       uint32
	numThreads uint32
	exeName    string
}

// windowsSnapshot is the concrete type returned by MakeGlobalSnapshot on Windows.
type windowsSnapshot struct {
	procs map[int32]*snapInfo
}

// MakeGlobalSnapshot enumerates all processes once via CreateToolhelp32Snapshot.
func MakeGlobalSnapshot() (any, error) {
	snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, 0)
	if err != nil {
		return nil, fmt.Errorf("procinfo: CreateToolhelp32Snapshot: %w", err)
	}
	defer windows.CloseHandle(snap)

	procs := make(map[int32]*snapInfo)

	var entry windows.ProcessEntry32
	entry.Size = uint32(unsafe.Sizeof(entry))

	if err := windows.Process32First(snap, &entry); err != nil {
		return nil, fmt.Errorf("procinfo: Process32First: %w", err)
	}
	for {
		pid := int32(entry.ProcessID)
		procs[pid] = &snapInfo{
			ppid:       entry.ParentProcessID,
			numThreads: entry.Threads,
			exeName:    windows.UTF16ToString(entry.ExeFile[:]),
		}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Read the wrapped %w error with errors.Is to identify the specific win32 error code
  2. Retry the call — transient failures can occur during heavy process create/destroy activity
  3. Run the process with sufficient privileges or adjust EDR/AV policy blocking snapshots
  4. Fall back to enumerating /proc-like data via other APIs (e.g. EnumProcesses) if snapshots are blocked

Example fix

// before
snap, err := procinfo.MakeGlobalSnapshot()
if err != nil {
	return err
}
// after
snap, err := procinfo.MakeGlobalSnapshot()
if err != nil {
	log.Printf("snapshot failed: %v; retrying once", err)
	snap, err = procinfo.MakeGlobalSnapshot()
	if err != nil {
		return fmt.Errorf("cannot enumerate processes: %w", err)
	}
}
Defensive patterns

Strategy: retry

Try / catch

snap, err := procinfo.MakeGlobalSnapshot()
if err != nil {
	var sysErr syscall.Errno
	if errors.As(err, &sysErr) && sysErr == windows.ERROR_ACCESS_DENIED {
		return fmt.Errorf("insufficient privileges to snapshot processes: %w", err)
	}
	time.Sleep(50 * time.Millisecond)
	snap, err = procinfo.MakeGlobalSnapshot()
}
if err != nil { return err }

Prevention

When it happens

Trigger: Calling MakeGlobalSnapshot when CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) fails — typically ERROR_ACCESS_DENIED from restrictive security software/policies, or when the calling process lacks sufficient privileges in hardened environments.

Common situations: Running under service accounts with stripped SeDebugPrivilege in locked-down environments; antivirus/EDR blocking toolhelp snapshots; extremely rare kernel resource exhaustion during process creation/destruction storms.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/935789626b7f4a57. Report an issue: GitHub.