gastownhall/beads · error
procid: get process times: %w
Error message
procid: get process times: %w
What it means
After confirming the process is still active, tokenForProcess reads the process creation time via windows.GetProcessTimes to build a unique, PID-reuse-resistant token. If GetProcessTimes fails, the error is wrapped and propagated to whichever public API (Capture/Verify/Open/Signal/verify) initiated the token mint.
Source
Thrown at internal/procid/procid_windows.go:134
func openProcess(pid int, access uint32) (windows.Handle, error) {
return windows.OpenProcess(access, false, uint32(pid))
}
func tokenForProcess(process windows.Handle) (Token, error) {
// An open handle keeps a terminated process's PID resolvable and its
// creation time readable, so check liveness explicitly before minting a
// token for it.
var code uint32
if err := windows.GetExitCodeProcess(process, &code); err != nil {
return "", fmt.Errorf("procid: get process exit code: %w", err)
}
if code != stillActive {
return "", errProcessExited
}
var created, exited, kernel, user windows.Filetime
if err := windows.GetProcessTimes(process, &created, &exited, &kernel, &user); err != nil {
return "", fmt.Errorf("procid: get process times: %w", err)
}
value := uint64(created.HighDateTime)<<32 | uint64(created.LowDateTime)
return Token("windows-v1:" + strconv.FormatUint(value, 10)), nil
}
// IsProcessGone reports whether err means the referenced process no longer
// exists.
func IsProcessGone(err error) bool {
return errors.Is(err, windows.ERROR_INVALID_PARAMETER) ||
errors.Is(err, errProcessExited)
}
View on GitHub (pinned to 71377f2769)
Solutions
- Verify the handle has PROCESS_QUERY_LIMITED_INFORMATION (procid.Capture already requests it)
- Check errors.Is(err, windows.ERROR_ACCESS_DENIED); run elevated or skip processes you cannot query
- Retry the Capture once after a short delay in case of transient handle invalidation
- Skip token capture for processes outside your ownership boundary instead of failing the whole batch
Example fix
// before
tok, err := procid.Capture(pid)
if err != nil { return err }
// after
tok, err := procid.Capture(pid)
if err != nil {
if errors.Is(err, windows.ERROR_ACCESS_DENIED) {
return nil // cannot token-capture protected process; skip
}
return err
} Defensive patterns
Strategy: retry
Validate before calling
if !canQuery(pid) {
return nil // cannot read process times; skip capture
} Type guard
func tokenCapturable(pid int) bool {
h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid))
if err != nil { return false }
_ = windows.CloseHandle(h)
return true
} Try / catch
var tok procid.Token
var err error
for i := 0; i < 2; i++ {
tok, err = procid.Capture(pid)
if err == nil || procid.IsProcessGone(err) || errors.Is(err, windows.ERROR_ACCESS_DENIED) {
break
}
time.Sleep(50 * time.Millisecond)
} Prevention
- Capture tokens for processes you own; skip SYSTEM/protected processes up front
- Retry once on transient handle failures before giving up
- Classify errors with procid.IsProcessGone and ERROR_ACCESS_DENIED instead of retrying blindly
- Keep procid library and golang.org/x/sys versions current
When it happens
Trigger: GetProcessTimes failing on a freshly opened handle — access denied on protected/system processes, or the handle was opened without query rights.
Common situations: Capturing tokens for SYSTEM or protected processes from a non-elevated daemon; handle rights reduced by third-party code; race where the handle was closed concurrently.
Related errors
- procid: terminate process: %w
- procid: get process exit code: %w
- failed to create .beads directory: %v Windows Controlled Fo
- finding process %d: %w
- sending SIGTERM to PID %d: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/9c05ac6d1340d491.
Report an issue: GitHub.