gastownhall/beads · error
authenticated proxy identity does not match its pidfile or…
Error message
authenticated proxy identity does not match its pidfile or workspace
What it means
This error is returned by readAndDial when the freshly spawned/authenticated proxy's handshake reply does not match the pidfile or workspace (mismatched PID, data/control port, or UpstreamID). It indicates the process that answered is not the proxy this workspace owns, so adoption is refused and the record is quarantined.
Solutions
- Let the library quarantine the record (default behavior) and retry the start to get a clean spawn on a fresh port
- Check for port collisions with other workspaces or leftover processes (lsof/ss) and kill/avoid them
- Ensure only one beads instance manages the workspace at a time (use the stop-epoch/lock protocol)
Example fix
// before
res := readAndDial(rootDir, pf)
// caller assumes success
// after
res := readAndDial(rootDir, pf)
if res.status == adoptionIdentityMismatch {
// library already quarantined the foreign record; restart cleanly
return spawnFreshProxy(rootDir)
} Defensive patterns
Strategy: type-guard
Validate before calling
// before adopting, verify the listener identity
if !pidMatchesPidfile(reply.PID, pf) || reply.UpstreamID != pf.UpstreamID { return errForeignListener } Type guard
func isIdentityMismatch(status adoptionStatus) bool { return status == adoptionIdentityMismatch } Try / catch
res := readAndDial(rootDir, pf)
if res.status == adoptionIdentityMismatch {
// record already quarantined; start a clean proxy
return spawnFreshProxy(rootDir)
} Prevention
- Ensure one workspace owner at a time
- Avoid overlapping port allocations between instances
- Clean stale pidfiles before start
When it happens
Trigger: readAndDial adoption probe where reply.PID, DataPort, ControlPort, or UpstreamID differs from the pidfile values; a foreign listener answering on the expected port.
Common situations: Another process grabbed the port between spawn and handshake; PID reuse; two workspaces/instances sharing a port range; stale pidfile pointing at someone else's server.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- ErrMissingBirth
- identity: invalid reply MAC
- identity: oversized reply
- identity: reply authentication failed
- identity: request refused
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/10e6ee014206d78a.
Report an issue: GitHub.
Appendix: source
Thrown at internal/storage/dbproxy/proxy/endpoint.go:631
reply, err := identity.Identify("127.0.0.1", pf.ControlPort, secret, identityProbeTimeout)
if err != nil {
return adoptionResult{status: adoptionIdentityMismatch, pidfile: pf, err: err}
}
// Accept schema v2 or newer, matching pidfile.ValidateV2's forward-compat
// policy for records.
if reply.Schema < pidfile.SchemaV2 ||
reply.Role != pidfile.KindProxy ||
reply.RootID != expectedRootID ||
reply.RootID != pf.RootID ||
reply.PID != pf.Pid ||
reply.Birth != pf.Birth ||
reply.DataPort != pf.Port ||
reply.ControlPort != pf.ControlPort ||
reply.UpstreamID != pf.UpstreamID {
return adoptionResult{
status: adoptionIdentityMismatch,
pidfile: pf,
err: errors.New("authenticated proxy identity does not match its pidfile or workspace"),
}
}
ep := Endpoint{Host: "127.0.0.1", Port: pf.Port}
if !probePort(ep, identityProbeTimeout) {
return adoptionResult{
status: adoptionIdentityMismatch,
pidfile: pf,
err: fmt.Errorf("authenticated proxy data port %d is not accepting connections", pf.Port),
}
}
return adoptionResult{status: adoptionAdopted, endpoint: ep, pidfile: pf}
}
func probePort(ep Endpoint, timeout time.Duration) bool {
conn, err := net.DialTimeout("tcp", ep.Address(), timeout)
if err != nil {
return falseView on GitHub (pinned to 71377f2769)