gastownhall/beads · error
discover proxy from %s: %w
Error message
discover proxy from %s: %w
What it means
GetCreateDatabaseProxyServerEndpoint could not read or parse the proxy PID record in the workspace because of an I/O error. Before any locking or spawning happens, the library reads the pidfile to discover an already-running proxy; if that read fails (as opposed to the record simply being absent), it fails fast rather than attempting to spawn a new proxy, because a corrupt or unreadable record could hide a live proxy. The underlying cause (permission, stale record, etc.) is wrapped via %w so errors.Is works.
Source
Thrown at internal/storage/dbproxy/proxy/endpoint.go:231
return Endpoint{}, fmt.Errorf("read proxy stop epoch: %w", err)
}
deadline := time.Now().Add(openDeadline)
timeout := time.NewTimer(openDeadline)
defer timeout.Stop()
poll := time.NewTicker(openPollInterval)
defer poll.Stop()
want := intendedUpstreamID(opts)
var lastSpawnErr error
for {
discovery := readAndDial(rootDir)
switch discovery.status {
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)View on GitHub (pinned to 71377f2769)
Solutions
- Check permissions on the pidfile at <rootDir>/proxy.pid (or wherever pidfile.Path points) and the parent directory; chown/chmod so the current user can read it
- If no proxy should be running, delete the unreadable record and retry
- Verify the filesystem is writable and not full (df, mount -o ro checks)
- If running in a container, ensure the same UID owns the workspace across invocations
Example fix
// before (running as wrong user) $ bd ready error: discover proxy from /work/.beads/proxy.pid: open ...: permission denied // after $ ls -l /work/.beads/proxy.pid $ sudo chown -R $(id -u) /work/.beads $ bd ready
Defensive patterns
Strategy: validation
Validate before calling
// Check the pidfile is readable before opening
pidPath := filepath.Join(rootDir, "proxy.pid")
if _, err := os.Stat(pidPath); err == nil {
f, err := os.Open(pidPath)
if err != nil { return fmt.Errorf("proxy record unreadable: %w", err) }
f.Close()
} Prevention
- Keep a single OS user for all bd invocations in a workspace
- Add a preflight readability check of the .beads directory in CI
- Avoid network filesystems for workspaces with background proxies
When it happens
Trigger: Calling GetCreateDatabaseProxyServerEndpoint (e.g. via bd open/serve flows) when pidfile.Path(rootDir, PIDFileName) exists but readAndDial cannot read it: permissions changed, the record is being written concurrently, or the file is unreadable due to disk/filesystem issues. Note this is an IO error only — a missing record proceeds to spawn instead.
Common situations: Running bd as a different user than the one that created the proxy record (root vs user in containers/CI); NFS or Docker volume mounts with permission quirks; disk-full or read-only filesystem; antivirus/file-watcher holding the file open.
Related errors
- discover proxy from %s under lock: %w
- probe proxy lock: %w
- failed to read backup state: %w
- failed to write temp file: %w
- failed to sync temp file: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/9580543208ace2ec.
Report an issue: GitHub.