gastownhall/beads · error

authenticated proxy identity does not match its pidfile or w

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.

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 false

View on GitHub (pinned to 71377f2769)

Solutions

  1. Let the library quarantine the record (default behavior) and retry the start to get a clean spawn on a fresh port
  2. Check for port collisions with other workspaces or leftover processes (lsof/ss) and kill/avoid them
  3. 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

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

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/10e6ee014206d78a. Report an issue: GitHub.