From da0785bd19d3b7e3010bd841b36d31aaf7b928bf Mon Sep 17 00:00:00 2001 From: Matt Van Horn Date: Tue, 19 May 2026 09:18:44 -0700 Subject: [PATCH] fix(daemon): recover acceptLoop panics (#1305) ## What? Adds a top-level `defer func() { recover(); log }()` to `(*Daemon).acceptLoop` so a panic in the accept goroutine logs with a stack trace instead of taking down the whole daemon. Closes #1119 ## Why? `daemon/daemon.go:120` launches `go d.acceptLoop()` with no panic recovery. A panic inside the loop (or in `addClient` / `daemonrpc.NewConn`) propagates up and the runtime aborts the process. The sync loop, IDLE watcher, and connected clients all die with it. The issue notes the same risk applies to the prior recovery fixes (#979 / #980 / #981). The recovery pattern matches the existing one at `main.go:4089`: log the panic with a `runtime/debug.Stack()` trace and let the goroutine exit. The daemon stops accepting new connections after a recovered panic, but already-connected clients and the background sync loop keep running, which is what the issue body asks for. --- daemon/daemon.go | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/daemon/daemon.go b/daemon/daemon.go index ac194ee1e60bec4b814ec0cb87bd1f11257a272d..20cdfb8897c3d138463c9f0024086d425b996b09 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -6,6 +6,7 @@ import ( "log" "net" "os" + "runtime/debug" "sort" "sync" "time" @@ -185,19 +186,30 @@ func (d *Daemon) initProviders() { func (d *Daemon) acceptLoop() { for { - conn, err := d.listener.Accept() - if err != nil { - select { - case <-d.shutdown: - return - default: - log.Printf("daemon: accept error: %v", err) - continue + done := func() bool { + defer func() { + if r := recover(); r != nil { + log.Printf("daemon: acceptLoop panic recovered: %v\n%s", r, debug.Stack()) + } + }() + conn, err := d.listener.Accept() + if err != nil { + select { + case <-d.shutdown: + return true + default: + log.Printf("daemon: accept error: %v", err) + return false + } } + rpcConn := daemonrpc.NewConn(conn) + d.addClient(rpcConn) + go d.handleClient(rpcConn) + return false + }() + if done { + return } - rpcConn := daemonrpc.NewConn(conn) - d.addClient(rpcConn) - go d.handleClient(rpcConn) } }