fix: use dbPath instead of socketPath for beadsDir derivation (#1064)

When using short socket paths (workspaces with paths >103 chars),
filepath.Dir(socketPath) returns /tmp/beads-XXXXX/ instead of the
actual .beads/ directory. This caused TryDaemonLock to look in the
wrong location, always return false, clean up the startlock, and
recurse infinitely causing stack overflow.

Changed 4 occurrences in acquireStartLock, handleStaleLock,
handleExistingSocket, and getPIDFileForSocket to use
filepath.Dir(dbPath) which correctly points to .beads/.

Fixes: gt-qlt

Co-authored-by: furiosa <mayor@gastown.local>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Zach Barahal
2026-01-12 23:06:00 -08:00
committed by GitHub
parent 17e7f53e15
commit 7de4b428f3

View File

@@ -235,7 +235,7 @@ func acquireStartLock(lockPath, socketPath string) bool {
// PID is alive - but is daemon actually running/starting?
// Use flock-based check as authoritative source (immune to PID reuse)
beadsDir := filepath.Dir(socketPath)
beadsDir := filepath.Dir(dbPath)
if running, _ := lockfile.TryDaemonLock(beadsDir); !running {
// Daemon lock not held - the start attempt failed or process was reused
debugLog("startlock PID %d alive but daemon lock not held, cleaning up", lockPID)
@@ -267,7 +267,7 @@ func handleStaleLock(lockPath, socketPath string) bool {
}
// PID is alive - but check daemon lock as authoritative source (immune to PID reuse)
beadsDir := filepath.Dir(socketPath)
beadsDir := filepath.Dir(dbPath)
if running, _ := lockfile.TryDaemonLock(beadsDir); !running {
debugLog("lock PID %d alive but daemon lock not held, removing and retrying", lockPID)
_ = os.Remove(lockPath)
@@ -291,7 +291,7 @@ func handleExistingSocket(socketPath string) bool {
// Use flock-based check as authoritative source (immune to PID reuse)
// If daemon lock is not held, daemon is definitely dead regardless of PID file
beadsDir := filepath.Dir(socketPath)
beadsDir := filepath.Dir(dbPath)
if running, pid := lockfile.TryDaemonLock(beadsDir); running {
debugLog("daemon lock held (PID %d), waiting for socket", pid)
return waitForSocketReadiness(socketPath, 5*time.Second)
@@ -400,8 +400,8 @@ func setupDaemonIO(cmd *exec.Cmd) {
// getPIDFileForSocket returns the PID file path for a given socket path
func getPIDFileForSocket(socketPath string) string {
// PID file is in same directory as socket, named daemon.pid
dir := filepath.Dir(socketPath)
// PID file is in .beads directory, not socket directory (socket may be in /tmp for short paths)
dir := filepath.Dir(dbPath)
return filepath.Join(dir, "daemon.pid")
}