ginuerzh/gost · warning
accept on closed listener
Error message
accept on closed listener
What it means
tunListener.Accept selects between an incoming connection and the closed channel; once Close() has closed the `closed` channel, Accept returns "accept on closed listener". It is the tun-device listener's way of signaling permanent shutdown.
Source
Thrown at tuntap.go:105
addrs, _ := ifce.Addrs()
log.Logf("[tun] %s: name: %s, mtu: %d, addrs: %s",
conn.LocalAddr(), ifce.Name, ifce.MTU, addrs)
ln.conns <- conn
}
return ln, nil
}
func (l *tunListener) Accept() (net.Conn, error) {
select {
case conn := <-l.conns:
return conn, nil
case <-l.closed:
}
return nil, errors.New("accept on closed listener")
}
func (l *tunListener) Addr() net.Addr {
return l.addr
}
func (l *tunListener) Close() error {
select {
case <-l.closed:
return errors.New("listener has been closed")
default:
close(l.closed)
}
return nil
}
type tunHandler struct {
options *HandlerOptionsView on GitHub (pinned to a33fdbf4c9)
Solutions
- Break out of the accept loop when this error is returned rather than retrying
- Stop the accept goroutine before or via the same signal that triggers Close
- Match on the error string (or wrap a sentinel) to distinguish shutdown from transient accept errors
Example fix
// before
conn, err := ln.Accept()
if err != nil { return err }
// after
conn, err := ln.Accept()
if err != nil {
if err.Error() == "accept on closed listener" { return nil }
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
// guard the accept loop with a shutdown flag set before ln.Close() var closing atomic.Bool
Try / catch
conn, err := ln.Accept()
if err != nil {
if strings.Contains(err.Error(), "closed listener") {
return nil // clean shutdown
}
return err
} Prevention
- Stop accept goroutines before closing the tunListener
- Treat this error as terminal; do not continue the loop
- Match the message or wrap a sentinel for clean-shutdown detection
When it happens
Trigger: Calling Accept on a tunListener after Close(); a Close/Accept race where the closed channel is closed while Accept is blocked in the select.
Common situations: Graceful shutdown of TUN-based tunnels; deferred Close firing while an accept loop is still running; test teardown closing listeners before drain goroutines exit.
Related errors
- accpet on closed listener
- accpet on closed listener
- accpet on closed listener
- listener has been closed
- closed
AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02).
Data as JSON: /api/errors/d873d2390ce9cb14.
Report an issue: GitHub.