gastownhall/beads · error

discover proxy from %s under lock: %w

Error message

discover proxy from %s under lock: %w

What it means

Same PID-record I/O failure as the lock-free discovery error, but detected after this caller successfully acquired proxy.lock and re-read the record. This proves the record was unreadable even while holding the exclusive lock, ruling out a concurrent replacement race, so the library returns immediately instead of spawning. The wrapped cause is preserved for errors.Is/As inspection.

Source

Thrown at internal/storage/dbproxy/proxy/endpoint.go:246

		case adoptionAdopted:
			return adoptedEndpoint(rootDir, want, discovery)
		case adoptionIOErr:
			return Endpoint{}, fmt.Errorf("discover proxy from %s: %w", pidfile.Path(rootDir, PIDFileName), discovery.err)
		}

		lock, err := util.TryLock(filepath.Join(rootDir, LockFileName))
		switch {
		case err == nil:
			// Re-read under proxy.lock. Another opener may have replaced the
			// record after our lock-free discovery.
			discovery = readAndDial(rootDir)
			if discovery.status == adoptionAdopted {
				lock.Unlock()
				return adoptedEndpoint(rootDir, want, discovery)
			}
			if discovery.status == adoptionIOErr {
				lock.Unlock()
				return Endpoint{}, fmt.Errorf("discover proxy from %s under lock: %w", pidfile.Path(rootDir, PIDFileName), discovery.err)
			}

			markerActive, markerErr := inspectSpawnMarkerLocked(rootDir)
			if markerErr != nil {
				lock.Unlock()
				return Endpoint{}, markerErr
			}
			if markerActive {
				lock.Unlock()
				lastSpawnErr = nil
				// This break exits the switch only; the outer discovery loop
				// continues with its normal bounded poll.
				break
			}

			var ep Endpoint
			ep, lastSpawnErr = spawnAndHandoff(rootDir, opts, deadline, stopEpoch, lock, discovery)
			if lastSpawnErr == nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Fix read permissions on the pidfile for the current user (chown/chmod)
  2. If the record is stale, remove it manually and retry the operation
  3. Check for a read-only or full filesystem on the workspace mount
  4. Run under the same user account that originally created the proxy

Example fix

// before
error: discover proxy from /repo/.beads/proxy.pid under lock: permission denied
// after
$ chmod u+r /repo/.beads/proxy.pid  # or rm -f if stale
$ bd ready
Defensive patterns

Strategy: validation

Validate before calling

// Under your own coordination, verify the record is readable before calling open
info, err := os.Stat(filepath.Join(rootDir, "proxy.pid"))
if err == nil && info.Mode().Perm()&0o400 == 0 {
    os.Chmod(filepath.Join(rootDir, "proxy.pid"), 0o644)
}

Prevention

When it happens

Trigger: GetCreateDatabaseProxyServerEndpoint acquired proxy.lock (no other opener held it), re-ran readAndDial, and the pidfile read still failed with an I/O error — e.g. the record exists but is unreadable due to permissions or filesystem errors.

Common situations: Mixed-UID ownership of the .beads workspace (agent runs as different user than initial setup); read-only bind mounts in CI containers; corrupted record from a partial write by an older binary.

Related errors


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