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

  1. Treat the single-connection contract: accept exactly one connection and stop the accept loop after the first success.
  2. Check listener liveness before each Accept and exit the loop when Close() has been called.
  3. Handle the returned error as a normal shutdown signal, not a failure.
  4. 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

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


AI-assisted analysis of go-delve/delve@a23773e6c3 (2026-08-31). Data as JSON: /api/errors/105956e02cc7dac1. Report an issue: GitHub.