ginuerzh/gost · warning
accpet on closed listener
Error message
accpet on closed listener
What it means
This error is returned by http2Listener.Accept() when the listener's errChan is closed (the listener has been shut down) and the channel yields a nil error. The sentinel message tells the caller that Accept can never yield new connections because the listener is closed. Note the message contains a typo ('accpet') which identifies this library's sentinel uniquely.
Source
Thrown at http2.go:650
w: w,
closed: make(chan struct{}),
}
select {
case l.connChan <- conn:
default:
log.Logf("[http2] %s - %s: connection queue is full", r.RemoteAddr, l.server.Addr)
return
}
<-conn.closed
}
func (l *http2Listener) Accept() (conn net.Conn, err error) {
select {
case conn = <-l.connChan:
case err = <-l.errChan:
if err == nil {
err = errors.New("accpet on closed listener")
}
}
return
}
func (l *http2Listener) Addr() net.Addr {
return l.addr
}
func (l *http2Listener) Close() (err error) {
select {
case <-l.errChan:
default:
err = l.server.Close()
l.errChan <- err
close(l.errChan)
}
return nilView on GitHub (pinned to a33fdbf4c9)
Solutions
- Stop the Accept loop when this error is returned — it is a permanent shutdown signal, not retryable.
- Ensure Close() is called only after the accept goroutine has been signaled to exit, or accept this error as the expected shutdown signal.
- Compare the error message (or wrap with errors.Is by wrapping the sentinel in the library) to distinguish shutdown from real failures.
- If using HTTP/2 muxing, prefer the h2Listener variant which signals closure via channel close rather than nil.
Example fix
// before
for {
conn, err := l.Accept()
if err != nil {
return fmt.Errorf("accept: %w", err) // aborts with scary message
}
}
// after
for {
conn, err := l.Accept()
if err != nil {
if strings.Contains(err.Error(), "closed listener") {
return nil // normal shutdown
}
return fmt.Errorf("accept: %w", err)
}
} Defensive patterns
Strategy: try-catch
Type guard
func isClosedListenerErr(err error) bool {
var opErr *net.OpError
return err != nil && (errors.Is(err, net.ErrClosed) || strings.Contains(err.Error(), "closed listener") || (errors.As(err, &opErr) && errors.Is(opErr.Err, net.ErrClosed)))
} Try / catch
conn, err := l.Accept()
if err != nil {
if isClosedListenerErr(err) {
return nil // clean shutdown
}
return err
} Prevention
- Exit accept loops on any Accept error rather than retrying blindly.
- Cancel the accept goroutine's context before calling Close().
- Match on the sentinel text when the library lacks exported sentinel errors.
When it happens
Trigger: Calling Accept() on an *http2Listener after Close() has been called: Close() closes errChan, so the select in Accept falls into the err branch, receives a zero-value nil, and replaces it with errors.New("accpet on closed listener").
Common situations: Accept loops that keep running after listener shutdown; race where Close() is called concurrently with a blocked Accept; server teardown code that does not stop its accept goroutine before closing the listener.
Related errors
AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02).
Data as JSON: /api/errors/eb9f1f8840deed61.
Report an issue: GitHub.