{"record":{"id":"b00c3089d70cf9bf","repo":"gastownhall/beads","slug":"proxy-forcestopunverified-inspect-executable-for","errorCode":null,"errorMessage":"proxy.ForceStopUnverified: inspect executable for pid %d: %w","messagePattern":"proxy\\.ForceStopUnverified: inspect executable for pid (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dbproxy/proxy/force_stop.go","lineNumber":208,"sourceCode":"\tif pid <= 0 {\n\t\treturn fmt.Errorf(\"proxy.ForceStopUnverified: record %s has invalid pid %d\", report.RecordPath, pid)\n\t}\n\t// One stable handle covers inspection and signaling, so the PID cannot be\n\t// recycled between the executable check and the kill on platforms with a\n\t// pinning primitive (Linux pidfd, Windows process handle).\n\tproc, gone, err := openUnverifiedProcess(pid)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"proxy.ForceStopUnverified: open pid %d: %w\", pid, err)\n\t}\n\tif gone {\n\t\treport.ProcessWasGone = true\n\t\treturn nil\n\t}\n\tdefer proc.close()\n\n\texecutable, gone, err := proc.executableBasename()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"proxy.ForceStopUnverified: inspect executable for pid %d: %w\", pid, err)\n\t}\n\tif gone {\n\t\treport.ProcessWasGone = true\n\t\treturn nil\n\t}\n\texecutable = normalizeForceStopExecutable(executable)\n\treport.Executable = executable\n\tif executable != \"bd\" && executable != \"dolt\" {\n\t\treturn fmt.Errorf(\n\t\t\t\"proxy.ForceStopUnverified: refusing to signal pid %d from %s: executable basename is %q, want bd or dolt\",\n\t\t\tpid,\n\t\t\treport.RecordPath,\n\t\t\texecutable,\n\t\t)\n\t}\n\n\t// Basename alone would let a recycled PID now running an unrelated bd or\n\t// dolt be killed; require the command line to tie the process to THIS","sourceCodeStart":190,"sourceCodeEnd":226,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/force_stop.go#L190-L226","documentation":"This error wraps a failure while reading the executable basename of the process holding the PID from an unverified (legacy or foreign) PID record during ForceStopUnverified. Before bd will signal any PID it did not verifiably own, it inspects /proc-style process metadata to confirm the binary is actually bd or dolt; if that inspection itself fails (permissions, procfs unavailable, race while the process exits), this error is returned and no signal is sent. It is a safety-gate failure, not a process kill failure.","triggerScenarios":"Calling proxy.ForceStopUnverified when the PID from the record cannot be inspected: the process exited between openUnverifiedProcess and executableBasename (race), the procfs entry lacks read permission, the process is a zombie/kernel thread with no exe link, or the platform lacks the required inspection primitive.","commonSituations":"Stale PID files pointing at processes that just exited; running bd under containers/restricted environments where /proc/<pid>/exe is unreadable or mounted noexec; hardened security modules (SELinux/AppArmor) blocking ptrace-like reads; inspecting system processes owned by another user.","solutions":["Re-run the command; if the PID belonged to an exiting process the retry will report ProcessWasGone and succeed","Check you can read /proc/<pid>/exe (or platform equivalent) for the target PID; if not, stop the process manually","If the record is stale, quarantine it: rename <record> to <record>.stale-<unix-timestamp>, then retry","Run bd as the same user that owns the target process, or inspect inside the container/namespace where it runs"],"exampleFix":"// before\nreport, err := proxy.ForceStopUnverified(ctx, rootDir, pidName, deadline)\nif err != nil { log.Fatal(err) }\n// after\nreport, err := proxy.ForceStopUnverified(ctx, rootDir, pidName, deadline)\nif err != nil {\n    if strings.Contains(err.Error(), \"inspect executable\") {\n        os.Rename(recordPath, recordPath+\".stale-\"+strconv.FormatInt(time.Now().Unix(), 10))\n        report, err = proxy.ForceStopUnverified(ctx, rootDir, pidName, deadline)\n    }\n    if err != nil { log.Fatal(err) }\n}","handlingStrategy":"try-catch","validationCode":"// pre-check the pid is inspectable before calling\npid := readPidFromRecord(recordPath)\nif pid > 0 {\n    if _, err := os.Readlink(fmt.Sprintf(\"/proc/%d/exe\", pid)); err != nil {\n        // process gone or unreadable: quarantine record first\n        os.Rename(recordPath, recordPath+\".stale-\"+fmt.Sprint(time.Now().Unix()))\n    }\n}","typeGuard":"func isInspectExecutableErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"inspect executable for pid\")\n}","tryCatchPattern":"report, err := proxy.ForceStopUnverified(ctx, rootDir, pidName, deadline)\nif isInspectExecutableErr(err) {\n    // transient (race) or permission issue: retry once, else quarantine\n    os.Rename(recordPath, recordPath+\".stale-\"+fmt.Sprint(time.Now().Unix()))\n    report, err = proxy.ForceStopUnverified(ctx, rootDir, pidName, deadline)\n}\nif err != nil { return err }","preventionTips":["Run bd client and daemons as the same OS user","Avoid unverified force-stop paths by keeping pid records in v2 (workspace-identity) format so proxy.Shutdown works","In containers, run inspection and daemon in the same PID namespace","Quarantine stale records with the .stale-<timestamp> rename instead of deleting them"],"tags":["process-inspection","pidfile","force-stop","permissions"],"backgroundTag":"process-inspection-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}