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

  1. 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
  2. If no proxy should be running, delete the unreadable record and retry
  3. Verify the filesystem is writable and not full (df, mount -o ro checks)
  4. 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

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


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