{"record":{"id":"eb9f1f8840deed61","repo":"ginuerzh/gost","slug":"accpet-on-closed-listener-eb9f1f","errorCode":null,"errorMessage":"accpet on closed listener","messagePattern":"accpet on closed listener","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"http2.go","lineNumber":650,"sourceCode":"\t\tw:      w,\n\t\tclosed: make(chan struct{}),\n\t}\n\tselect {\n\tcase l.connChan <- conn:\n\tdefault:\n\t\tlog.Logf(\"[http2] %s - %s: connection queue is full\", r.RemoteAddr, l.server.Addr)\n\t\treturn\n\t}\n\n\t<-conn.closed\n}\n\nfunc (l *http2Listener) Accept() (conn net.Conn, err error) {\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase err = <-l.errChan:\n\t\tif err == nil {\n\t\t\terr = errors.New(\"accpet on closed listener\")\n\t\t}\n\t}\n\treturn\n}\n\nfunc (l *http2Listener) Addr() net.Addr {\n\treturn l.addr\n}\n\nfunc (l *http2Listener) Close() (err error) {\n\tselect {\n\tcase <-l.errChan:\n\tdefault:\n\t\terr = l.server.Close()\n\t\tl.errChan <- err\n\t\tclose(l.errChan)\n\t}\n\treturn nil","sourceCodeStart":632,"sourceCodeEnd":668,"githubUrl":"https://github.com/ginuerzh/gost/blob/a33fdbf4c98034f4bfeeaea9868909822b9c526d/http2.go#L632-L668","documentation":"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.","triggerScenarios":"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\").","commonSituations":"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.","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."],"exampleFix":"// before\nfor {\n    conn, err := l.Accept()\n    if err != nil {\n        return fmt.Errorf(\"accept: %w\", err) // aborts with scary message\n    }\n}\n// after\nfor {\n    conn, err := l.Accept()\n    if err != nil {\n        if strings.Contains(err.Error(), \"closed listener\") {\n            return nil // normal shutdown\n        }\n        return fmt.Errorf(\"accept: %w\", err)\n    }\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isClosedListenerErr(err error) bool {\n    var opErr *net.OpError\n    return err != nil && (errors.Is(err, net.ErrClosed) || strings.Contains(err.Error(), \"closed listener\") || (errors.As(err, &opErr) && errors.Is(opErr.Err, net.ErrClosed)))\n}","tryCatchPattern":"conn, err := l.Accept()\nif err != nil {\n    if isClosedListenerErr(err) {\n        return nil // clean shutdown\n    }\n    return err\n}","preventionTips":["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."],"tags":["network","http2","listener-closed","accept-loop"],"backgroundTag":"use-of-closed-listener","analyzedSha":"a33fdbf4c98034f4bfeeaea9868909822b9c526d","analyzedAt":"2026-09-02T22:15:54.506Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}