ginuerzh/gost · warning

accpet on closed listener

Error message

accpet on closed listener

What it means

mtlsListener.Accept blocks on connChan/errChan; if errChan is closed (meaning the listener was shut down), it returns a literal "accpet on closed listener" error (note the upstream typo). This is the standard Go convention for Accept on a closed listener and signals that no further connections will ever be produced.

Source

Thrown at tls.go:244

		}

		cc := &muxStreamConn{Conn: conn, stream: stream}
		select {
		case l.connChan <- cc:
		default:
			cc.Close()
			log.Logf("[mtls] %s - %s: connection queue is full", conn.RemoteAddr(), conn.LocalAddr())
		}
	}
}

func (l *mtlsListener) Accept() (conn net.Conn, err error) {
	var ok bool
	select {
	case conn = <-l.connChan:
	case err, ok = <-l.errChan:
		if !ok {
			err = errors.New("accpet on closed listener")
		}
	}
	return
}
func (l *mtlsListener) Addr() net.Addr {
	return l.ln.Addr()
}

func (l *mtlsListener) Close() error {
	return l.ln.Close()
}

// Wrap a net.Conn into a client tls connection, performing any
// additional verification as needed.
//
// As of go 1.3, crypto/tls only supports either doing no certificate
// verification, or doing full verification including of the peer's
// DNS name. For consul, we want to validate that the certificate is

View on GitHub (pinned to a33fdbf4c9)

Solutions

  1. Treat this error as a terminal shutdown signal: exit the accept loop instead of logging it as a failure
  2. Ensure Close is called exactly once and after the accept goroutine has been signaled to stop
  3. Use net.ErrClosed-style sentinel comparison (errors.Is / string match) to filter this error from real accept failures

Example fix

// before
for {
    conn, err := ln.Accept()
    if err != nil { log.Fatal(err) }
}
// after
for {
    conn, err := ln.Accept()
    if err != nil {
        if strings.Contains(err.Error(), "closed listener") { return nil }
        log.Fatal(err)
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// no pre-call validation possible; guard the accept loop instead
running := atomic.Bool{}
running.Store(true)
// set running.Store(false) before calling ln.Close()

Try / catch

for running.Load() {
    conn, err := ln.Accept()
    if err != nil {
        if strings.Contains(err.Error(), "closed listener") { break } // expected shutdown
        log.Printf("accept: %v", err); continue
    }
    go handle(conn)
}

Prevention

When it happens

Trigger: Calling Accept on an mtlsListener after Close() has been invoked (which closes the channels), or racing Close with a pending Accept in an accept loop.

Common situations: Server shutdown sequences where the accept goroutine hasn't exited before Close is called; double-Close in defer handlers; restarting the listener while the old accept loop is still draining.

Related errors


AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02). Data as JSON: /api/errors/fa15140c4a6d6cec. Report an issue: GitHub.