{"record":{"id":"47b5d964479b83f5","repo":"gastownhall/beads","slug":"proxy-forcestopunverified-confirm-pid-d-exit-w","errorCode":null,"errorMessage":"proxy.ForceStopUnverified: confirm pid %d exit: %w","messagePattern":"proxy\\.ForceStopUnverified: confirm pid (.+?) exit: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/force_stop.go","lineNumber":268,"sourceCode":"\t\t\treport.RecordPath,\n\t\t\treport.RecordPath,\n\t\t)\n\t}\n\n\tgone, err = proc.kill()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"proxy.ForceStopUnverified: signal pid %d: %w\", pid, err)\n\t}\n\tif gone {\n\t\treport.ProcessWasGone = true\n\t\treturn nil\n\t}\n\treport.SignalSent = true\n\n\tfor {\n\t\texited, err := proc.exited()\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"proxy.ForceStopUnverified: confirm pid %d exit: %w\", pid, err)\n\t\t}\n\t\tif exited {\n\t\t\treturn nil\n\t\t}\n\t\tif time.Now().After(deadline) {\n\t\t\treturn fmt.Errorf(\"proxy.ForceStopUnverified: timeout waiting for pid %d to exit\", pid)\n\t\t}\n\t\ttime.Sleep(shutdownConfirmPoll)\n\t}\n}\n\nfunc normalizeForceStopExecutable(name string) string {\n\tname = strings.TrimSpace(filepath.Base(name))\n\tname = strings.TrimSuffix(name, \" (deleted)\")\n\tname = strings.TrimSuffix(strings.ToLower(name), \".exe\")\n\treturn name\n}\n","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/force_stop.go#L250-L286","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-run `bd dolt stop --force`; the recorded process is usually gone by then and the next run takes the ProcessWasGone path","Check the wrapped cause (%w) to identify the OS-level failure (e.g. /proc read permission in containers) and fix that environment issue","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","If the process is confirmed dead but the record remains, rename proxy.pid to proxy.pid.stale-<unix-timestamp> to quarantine it manually"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Confirm the PID is gone before/instead of relying on one force-stop run:\nalive, err := syscall.Kill(pid, 0)\nprocessGone := err == syscall.ESRCH","typeGuard":null,"tryCatchPattern":"report, err := proxy.ForceStopUnverified(rootDir)\nif err != nil && strings.Contains(err.Error(), \"confirm pid\") {\n    // Signal was sent; wait and retry once — the process is likely gone now\n    time.Sleep(2 * time.Second)\n    report, err = proxy.ForceStopUnverified(rootDir)\n}","preventionTips":["Use the default timeout rather than an aggressive custom one so polling has room to complete","Run force-stop in normal (not containerized/restricted /proc) environments where process status reads are reliable","Check report.SignalSent on partial failure to know the kill already happened before retrying","Treat any force-stop error as recoverable-by-retry: the second run re-inspects and takes the already-gone fast path"],"tags":["process-management","go","pidfile","force-stop"],"backgroundTag":"process-exit-confirm-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}