{"record":{"id":"c614fabc44763969","repo":"gastownhall/beads","slug":"procid-process-no-longer-matches-token","errorCode":null,"errorMessage":"procid: process no longer matches token","messagePattern":"procid: process no longer matches token","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/procid/procid_windows.go","lineNumber":112,"sourceCode":"func (h *Handle) Close() error {\n\tif h.process == 0 {\n\t\treturn nil\n\t}\n\terr := windows.CloseHandle(h.process)\n\th.process = 0\n\tif err != nil {\n\t\treturn fmt.Errorf(\"procid: close process handle: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc (h *Handle) verify() error {\n\tcurrent, err := tokenForProcess(h.process)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif current != h.token {\n\t\treturn fmt.Errorf(\"procid: process no longer matches token\")\n\t}\n\treturn nil\n}\n\nfunc openProcess(pid int, access uint32) (windows.Handle, error) {\n\treturn windows.OpenProcess(access, false, uint32(pid))\n}\n\nfunc tokenForProcess(process windows.Handle) (Token, error) {\n\t// An open handle keeps a terminated process's PID resolvable and its\n\t// creation time readable, so check liveness explicitly before minting a\n\t// token for it.\n\tvar code uint32\n\tif err := windows.GetExitCodeProcess(process, &code); err != nil {\n\t\treturn \"\", fmt.Errorf(\"procid: get process exit code: %w\", err)\n\t}\n\tif code != stillActive {\n\t\treturn \"\", errProcessExited","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/procid/procid_windows.go#L94-L130","documentation":"Handle.verify (used by Signal before terminating) re-mints the token from the held process handle and finds it differs from the token captured at Open time. The library refuses to signal: the handle is believed to refer to a process instance other than the one originally opened, i.e. suspected PID reuse or stale state.","triggerScenarios":"Calling Handle.Signal/Kill after the target process exited and its PID was recycled while some other handle kept the object alive; external code replaced or corrupted the handle's token field.","commonSituations":"Holding a Handle for a long time across process restarts; the process exited but a third party (Task Manager, antivirus) keeps the process object alive so the handle still resolves.","solutions":["Call Handle.Close and re-run procid.Capture/Open to obtain a fresh, consistent handle+token pair","Use procid.IsProcessGone on the error from Signal to distinguish exited targets (errProcessExited is returned separately and Signal treats it as success)","Shorten the lifetime of held handles; open, signal, close instead of holding indefinitely","If you see this repeatedly, verify tokens aren't being persisted/reloaded across process restarts"],"exampleFix":"// before\nif err := h.Kill(); err != nil { return err }\n// after\nif err := h.Kill(); err != nil {\n    _ = h.Close()\n    return fmt.Errorf(\"stale handle for pid %d, re-open required: %w\", pid, err)\n}","handlingStrategy":"fallback","validationCode":"// Before signaling a long-held handle, re-verify the token still matches\n// (verify() runs inside Signal; pre-check externally with Verify)\nok, err := procid.Verify(pid, tok)\nif err != nil || !ok {\n    return nil // stale; do not signal\n}","typeGuard":"func handleIsCurrent(h *procid.Handle, pid int, tok procid.Token) bool {\n    ok, err := procid.Verify(pid, tok)\n    return err == nil && ok\n}","tryCatchPattern":"if err := h.Kill(); err != nil {\n    _ = h.Close()\n    // rebuild handle+token pair and retry once\n    newTok, cerr := procid.Capture(pid)\n    if cerr != nil { return cerr }\n    nh, oerr := procid.Open(pid, newTok)\n    if oerr != nil { return oerr }\n    return nh.Kill()\n}","preventionTips":["Prefer open→signal→close over holding handles for long periods","Re-capture tokens whenever the target may have restarted","Note Signal returns nil for already-exited targets; only token mismatch indicates stale identity","Never mutate Handle fields outside the library"],"tags":["windows","process","pid-reuse","token-mismatch"],"backgroundTag":"pid-reuse-token-mismatch","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}