go-delve/delve · info
accept failed: listener closed
Error message
accept failed: listener closed
What it means
preconnectedListener.Accept returns this error when the listener has already handed out its single pre-connected conn and has been closed. After the first Accept succeeds, Accept blocks on closech and only unblocks when Close() is called, at which point it reports that the listener is closed and can no longer accept.
Source
Thrown at service/listenerpipe.go:42
type preconnectedListener struct {
accepted bool
conn net.Conn
closech chan struct{}
closeMu sync.Mutex
acceptMu sync.Mutex
}
// Accept returns the pre-established connection the first time it's called,
// it blocks until the listener is closed on every subsequent call.
func (l *preconnectedListener) Accept() (net.Conn, error) {
l.acceptMu.Lock()
defer l.acceptMu.Unlock()
if !l.accepted {
l.accepted = true
return l.conn, nil
}
<-l.closech
return nil, errors.New("accept failed: listener closed")
}
// Close closes the listener.
func (l *preconnectedListener) Close() error {
l.closeMu.Lock()
defer l.closeMu.Unlock()
if l.closech == nil {
return nil
}
close(l.closech)
l.closech = nil
return nil
}
// Addr returns the listener's network address.
func (l *preconnectedListener) Addr() net.Addr {
return l.conn.LocalAddr()
}View on GitHub (pinned to a23773e6c3)
Solutions
- Treat the single-connection contract: accept exactly one connection and stop the accept loop after the first success.
- Check listener liveness before each Accept and exit the loop when Close() has been called.
- Handle the returned error as a normal shutdown signal, not a failure.
- If more connections are needed, use net.Listen instead of the preconnected listener.
Example fix
// before
for {
conn, err := l.Accept()
if err != nil { log.Fatal(err) }
go handle(conn)
}
// after
for {
conn, err := l.Accept()
if err != nil {
if strings.Contains(err.Error(), "listener closed") {
return // normal shutdown
}
log.Fatal(err)
}
go handle(conn)
break // preconnected listener serves one connection
} Defensive patterns
Strategy: try-catch
Validate before calling
// no pre-validation possible; track listener state yourself
if l.isClosed() { return } // check before looping Accept Try / catch
conn, err := l.Accept()
if err != nil {
if strings.Contains(err.Error(), "listener closed") {
return // clean shutdown, not a failure
}
return err
} Prevention
- Accept only once per preconnectedListener; serve exactly one connection.
- Stop accept loops when the debugger/server is shutting down.
- Log this error at info level, not as a crash.
When it happens
Trigger: Calling Accept more than once on a preconnectedListener: the first call returns the wrapped conn, and any subsequent call blocks until Close() is invoked, then returns 'accept failed: listener closed'. Also occurs if Accept is called on a listener that was closed before the single connection was consumed.
Common situations: Client/server code built on listenerpipe (e.g. Delve's DAP or RPC in-process piping) where an accept loop keeps calling Accept after the single expected connection was served, or the server side shuts down while the accept loop is still running.
Related errors
- short read
- can not continue execution of core process
- can not change register values of core process
- unrecognized core format
- cannot write a breakpoint to a core file
AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31).
Data as JSON: /api/errors/105956e02cc7dac1.
Report an issue: GitHub.