{"record":{"id":"f1238f4bd14dce99","repo":"ginuerzh/gost","slug":"accpet-on-closed-listener-f1238f","errorCode":null,"errorMessage":"accpet on closed listener","messagePattern":"accpet on closed listener","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kcp.go","lineNumber":401,"sourceCode":"\t\t}\n\n\t\tcc := &muxStreamConn{Conn: conn, stream: stream}\n\t\tselect {\n\t\tcase l.connChan <- cc:\n\t\tdefault:\n\t\t\tcc.Close()\n\t\t\tlog.Logf(\"[kcp] %s - %s: connection queue is full\", conn.RemoteAddr(), conn.LocalAddr())\n\t\t}\n\t}\n}\n\nfunc (l *kcpListener) Accept() (conn net.Conn, err error) {\n\tvar ok bool\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase err, ok = <-l.errChan:\n\t\tif !ok {\n\t\t\terr = errors.New(\"accpet on closed listener\")\n\t\t}\n\t}\n\treturn\n}\nfunc (l *kcpListener) Addr() net.Addr {\n\treturn l.ln.Addr()\n}\n\nfunc (l *kcpListener) Close() error {\n\treturn l.ln.Close()\n}\n\nfunc blockCrypt(key, crypt, salt string) (block kcp.BlockCrypt) {\n\tpass := pbkdf2.Key([]byte(key), []byte(salt), 4096, 32, sha1.New)\n\n\tswitch crypt {\n\tcase \"sm4\":\n\t\tblock, _ = kcp.NewSM4BlockCrypt(pass[:16])","sourceCodeStart":383,"sourceCodeEnd":419,"githubUrl":"https://github.com/ginuerzh/gost/blob/a33fdbf4c98034f4bfeeaea9868909822b9c526d/kcp.go#L383-L419","documentation":"kcpListener.Accept returns this error when the listener's errChan is closed, which happens after the listener has been closed. The (typo'd) message indicates Accept was invoked on a listener that is no longer open. It signals the accept loop to stop.","triggerScenarios":"Calling Accept on a *kcpListener after Close() was called; concurrent goroutines still running accept loops when the listener is shut down; race between Close and a pending Accept.","commonSituations":"Server shutdown sequences where Accept loops aren't coordinated with Close; health-check or watchdog goroutines calling Accept during restart; accepting in multiple goroutines while the main thread closes the listener.","solutions":["Stop the Accept loop when this error is returned (it means the listener is closed) — do not retry.","Synchronize Close with accept goroutines (context cancel, sync.WaitGroup) so Accept is not called after Close.","Treat the error as io.EOF-like termination in server code: return/exit instead of logging-and-continuing.","Guard with a closed flag/atomic so Accept calls after Close are never issued."],"exampleFix":"// before\nfor {\n    conn, err := ln.Accept()\n    if err != nil {\n        log.Println(err)\n        continue // infinite error spam after Close\n    }\n    go handle(conn)\n}\n// after\nfor {\n    conn, err := ln.Accept()\n    if err != nil {\n        if errors.Is(err, errClosedListener) || strings.Contains(err.Error(), \"closed listener\") {\n            return // listener closed\n        }\n        log.Println(err)\n        continue\n    }\n    go handle(conn)\n}","handlingStrategy":"try-catch","validationCode":"select {\ncase <-lnClosed:\n\treturn errors.New(\"listener already closed\")\ndefault:\n}\n// then call Accept","typeGuard":"func isClosedListenerErr(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"closed listener\")\n}","tryCatchPattern":"conn, err := ln.Accept()\nif err != nil {\n\tif isClosedListenerErr(err) {\n\t\treturn nil // graceful shutdown\n\t}\n\tlog.Printf(\"accept error: %v\", err)\n\tcontinue\n}","preventionTips":["Exit the accept loop on this error instead of retrying.","Coordinate listener Close with accept goroutines via context or WaitGroup.","Only call Accept from a single owner goroutine with a defined shutdown order."],"tags":["kcp","listener","accept","closed-resource"],"backgroundTag":"accept-on-closed-listener","analyzedSha":"a33fdbf4c98034f4bfeeaea9868909822b9c526d","analyzedAt":"2026-09-02T22:15:54.506Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}