gastownhall/beads · error

proxy.ForceStopUnverified: confirm pid %d exit: %w

Error message

proxy.ForceStopUnverified: confirm pid %d exit: %w

What it means

This error is returned by inspectAndStopUnverifiedPID inside ForceStopUnverified (`bd dolt stop --force`) after SIGKILL has already been sent to the recorded unverified process, when polling the process handle to confirm it actually exited fails. It is not a timeout — the exit-check API itself returned an error (e.g. the process handle became unusable or the OS query failed). The wrapped cause (%w) carries the underlying OS-level reason.

Source

Thrown at internal/storage/dbproxy/proxy/force_stop.go:268

			report.RecordPath,
			report.RecordPath,
		)
	}

	gone, err = proc.kill()
	if err != nil {
		return fmt.Errorf("proxy.ForceStopUnverified: signal pid %d: %w", pid, err)
	}
	if gone {
		report.ProcessWasGone = true
		return nil
	}
	report.SignalSent = true

	for {
		exited, err := proc.exited()
		if err != nil {
			return fmt.Errorf("proxy.ForceStopUnverified: confirm pid %d exit: %w", pid, err)
		}
		if exited {
			return nil
		}
		if time.Now().After(deadline) {
			return fmt.Errorf("proxy.ForceStopUnverified: timeout waiting for pid %d to exit", pid)
		}
		time.Sleep(shutdownConfirmPoll)
	}
}

func normalizeForceStopExecutable(name string) string {
	name = strings.TrimSpace(filepath.Base(name))
	name = strings.TrimSuffix(name, " (deleted)")
	name = strings.TrimSuffix(strings.ToLower(name), ".exe")
	return name
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Re-run `bd dolt stop --force`; the recorded process is usually gone by then and the next run takes the ProcessWasGone path
  2. Check the wrapped cause (%w) to identify the OS-level failure (e.g. /proc read permission in containers) and fix that environment issue
  3. Verify the process state manually (ps -p <pid>) and, if it is a zombie, reap its parent or wait for init to reap it before retrying
  4. If the process is confirmed dead but the record remains, rename proxy.pid to proxy.pid.stale-<unix-timestamp> to quarantine it manually
Defensive patterns

Strategy: retry

Validate before calling

// Confirm the PID is gone before/instead of relying on one force-stop run:
alive, err := syscall.Kill(pid, 0)
processGone := err == syscall.ESRCH

Try / catch

report, err := proxy.ForceStopUnverified(rootDir)
if err != nil && strings.Contains(err.Error(), "confirm pid") {
    // Signal was sent; wait and retry once — the process is likely gone now
    time.Sleep(2 * time.Second)
    report, err = proxy.ForceStopUnverified(rootDir)
}

Prevention

When it happens

Trigger: Call ForceStopUnverified (or `bd dolt stop --force`) on a legacy pre-v2 proxy.pid or proxy-child.pid record whose PID is live, passes the bd/dolt executable and workspace-scope checks, gets signaled, and then proc.exited() fails on a subsequent poll iteration — i.e. the error occurs only after report.SignalSent was set to true.

Common situations: Zombie/defunct processes whose wait status cannot be read; platform handle issues where the pidfd/process handle becomes invalid mid-poll; containerized or restricted /proc environments where process status reads fail intermittently; extremely long-lived polling loops racing with system-level process cleanup.

Related errors


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