{"record":{"id":"4fb9b9369ac6cd9d","repo":"gastownhall/beads","slug":"errunverifiableprocess","errorCode":"ErrUnverifiableProcess","errorMessage":"proxy process identity is unverifiable","messagePattern":"proxy process identity is unverifiable","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/storage/dbproxy/proxy/endpoint.go","lineNumber":98,"sourceCode":"\tquarantineRetention = 30 * 24 * time.Hour\n\n\tspawnMarkerFileName = \"proxy.spawn\"\n\tstopEpochFileName   = \"proxy.stop-epoch\"\n)\n\nvar ResolveExecutable = os.Executable\n\nvar (\n\tverifyProcessIdentity = procid.Verify\n\tresolveRootIdentity   = identity.RootID\n\treadControlSecret     = identity.ReadSecret\n)\n\n// ErrUnverifiableProcess marks lifecycle operations that refuse to signal a\n// process because its workspace-scoped identity cannot be verified. Callers\n// may use errors.Is to offer a narrower recovery path without treating\n// unrelated shutdown failures as identity failures.\nvar ErrUnverifiableProcess = errors.New(\"proxy process identity is unverifiable\")\n\ntype unverifiableLifecycleError struct {\n\tmessage string\n}\n\nfunc (e *unverifiableLifecycleError) Error() string {\n\treturn e.message\n}\n\nfunc (e *unverifiableLifecycleError) Unwrap() error {\n\treturn ErrUnverifiableProcess\n}\n\nvar stopEpochSequence atomic.Uint64\n\n// beforeProxyChildStart is a deterministic test hook for the otherwise tiny\n// release-lock-before-exec scheduling window. Production leaves it as a no-op.\nvar beforeProxyChildStart = func() {}","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dbproxy/proxy/endpoint.go#L80-L116","documentation":"ErrUnverifiableProcess marks lifecycle operations (stop/force-stop) that refuse to signal a process because its workspace-scoped identity (pidfile PID + birth token) could not be verified. It is wrapped by unverifiableLifecycleError which supplies the detail message; callers use errors.Is to offer a narrower recovery path without misclassifying unrelated shutdown failures.","triggerScenarios":"Attempting to stop a proxy whose live process cannot be matched against the pidfile's birth token; shutdown hitting a foreign or recycled-PID process; CanForceStopUnverified gating refusals for unverified records.","commonSituations":"PID reuse after a crash so the pidfile PID now belongs to an unrelated process; shared workspace directory with another checkout's proxy; stale pidfile pointing at a foreign listener.","solutions":["Treat it as a safety refusal: use errors.Is(err, ErrUnverifiableProcess) and take the explicit force-stop/unverified recovery path the API exposes","Remove the stale pidfile so the next start does a clean spawn instead of trying to stop an unverified process","Investigate what process holds the port before force-stopping; do not blindly kill"],"exampleFix":"// before\nif err := ep.Stop(ctx); err != nil { return err }\n// after\nif err := ep.Stop(ctx); err != nil {\n    if errors.Is(err, endpoint.ErrUnverifiableProcess) {\n        return ep.ForceStopUnverified(ctx) // explicit user-approved path\n    }\n    return err\n}","handlingStrategy":"type-guard","validationCode":"if !pidfileMatchesProcess(pf) { /* skip or quarantine instead of signaling */ }","typeGuard":"func isUnverifiable(err error) bool { return errors.Is(err, endpoint.ErrUnverifiableProcess) }","tryCatchPattern":"if err := ep.Stop(ctx); err != nil {\n    if errors.Is(err, endpoint.ErrUnverifiableProcess) {\n        return handleUnverified(ep) // explicit recovery path\n    }\n    return err\n}","preventionTips":["Avoid PID reuse confusion by cleaning stale pidfiles promptly","One manager per workspace","Never signal a process you have not verified against pidfile+birth"],"tags":["process","identity","shutdown","safety"],"backgroundTag":"unverifiable-process-identity","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}