wavetermdev/waveterm · error

procinfo: Process32First: %w

Error message

procinfo: Process32First: %w

What it means

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.

Source

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

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[:]),
		}
		if err := windows.Process32Next(snap, &entry); err != nil {
			if errors.Is(err, windows.ERROR_NO_MORE_FILES) {
				break
			}
			return nil, fmt.Errorf("procinfo: Process32Next: %w", err)
		}
	}

	return &windowsSnapshot{procs: procs}, nil
}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Ensure the underlying entry.Size is set to uint32(unsafe.Sizeof(entry)) before the call (library already does this; check vendored forks)
  2. Inspect the wrapped win32 error (errors.Is on windows.ERROR_ACCESS_DENIED etc.)
  3. Retry MakeGlobalSnapshot once — the failure is usually transient or environmental
  4. Update the golang.org/x/sys/windows package if the version has known Process32First bugs

Example fix

// after (caller retry pattern)
var snap any
var err error
for i := 0; i < 2; i++ {
	snap, err = procinfo.MakeGlobalSnapshot()
	if err == nil {
		break
	}
}
if err != nil {
	return fmt.Errorf("process snapshot unavailable: %w", err)
}
Defensive patterns

Strategy: retry

Try / catch

snap, err := procinfo.MakeGlobalSnapshot()
if err != nil {
	if strings.Contains(err.Error(), "Process32First") {
		snap, err = procinfo.MakeGlobalSnapshot() // single retry
	}
	if err != nil { return err }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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