gastownhall/beads · error

open process %d: %w

Error message

open process %d: %w

What it means

On Windows, openUnverifiedProcess opens a handle to the PID via OpenProcess to inspect it before any kill. ERROR_INVALID_PARAMETER is treated as 'process gone'; any other OpenProcess failure is wrapped in this error, aborting the force-stop so the proxy never acts on an unverified PID.

Source

Thrown at internal/storage/dbproxy/proxy/unverified_process_windows.go:38

// from recycling the PID underneath us.
type unverifiedProcess struct {
	pid    int
	handle windows.Handle
}

// openUnverifiedProcess opens a stable handle for pid. gone reports a PID
// that no longer exists or has already terminated.
func openUnverifiedProcess(pid int) (proc *unverifiedProcess, gone bool, err error) {
	handle, err := windows.OpenProcess(
		windows.PROCESS_QUERY_LIMITED_INFORMATION|windows.PROCESS_TERMINATE,
		false,
		uint32(pid),
	)
	if err != nil {
		if errors.Is(err, windows.ERROR_INVALID_PARAMETER) {
			return nil, true, nil
		}
		return nil, false, fmt.Errorf("open process %d: %w", pid, err)
	}
	exited, err := handleExited(handle)
	if err != nil {
		_ = windows.CloseHandle(handle)
		return nil, false, err
	}
	if exited {
		_ = windows.CloseHandle(handle)
		return nil, true, nil
	}
	return &unverifiedProcess{pid: pid, handle: handle}, false, nil
}

func (p *unverifiedProcess) executableBasename() (basename string, gone bool, err error) {
	buffer := make([]uint16, 32768)
	size := uint32(len(buffer))
	if err := windows.QueryFullProcessImageName(p.handle, 0, &buffer[0], &size); err != nil {
		return "", false, fmt.Errorf("query image name for pid %d: %w", p.pid, err)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Run the stop command from an elevated (Administrator) terminal after confirming the PID's identity in Task Manager or `tasklist /FI "PID eq <pid>"`.
  2. If the PID was recycled to an unrelated process, do not kill it — delete the stale pidfile and restart.
  3. Check antivirus/EDR policies that block OpenProcess and add an exclusion for bd.
  4. Reboot or log off stale sessions to clear recycled-PID conflicts, then retry.

Example fix

// before: access denied opening elevated daemon's PID
# run non-elevated
bd ...
// after: elevated terminal
Start-Process -Verb RunAs bd ... # or taskkill /PID <pid> /F after verifying
Defensive patterns

Strategy: try-catch

Validate before calling

out, err := exec.Command("tasklist", "/FI", fmt.Sprintf("PID eq %d", pid), "/FO", "CSV", "/NH").Output()
if err == nil && !strings.Contains(string(out), expectedImageName) {
    // PID recycled to an unrelated process: do not open/kill
}

Try / catch

proc, gone, err := openUnverifiedProcess(pid)
if err != nil {
    if errors.Is(err, windows.ERROR_ACCESS_DENIED) {
        // needs elevation or PID owned by another user: verify first
    }
    return err
}

Prevention

When it happens

Trigger: openUnverifiedProcess calls windows.OpenProcess and receives an error other than ERROR_INVALID_PARAMETER — most commonly ERROR_ACCESS_DENIED (process owned by another user/service, e.g. a recycled PID now owned by SYSTEM).

Common situations: The recorded daemon PID was recycled by a Windows service or another user's process (access denied); running without elevation when the daemon ran elevated; antivirus/EDR blocking handle opens.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/297ed8069a004739. Report an issue: GitHub.