{"record":{"id":"d873d2390ce9cb14","repo":"ginuerzh/gost","slug":"accept-on-closed-listener","errorCode":null,"errorMessage":"accept on closed listener","messagePattern":"accept on closed listener","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"tuntap.go","lineNumber":105,"sourceCode":"\n\t\taddrs, _ := ifce.Addrs()\n\t\tlog.Logf(\"[tun] %s: name: %s, mtu: %d, addrs: %s\",\n\t\t\tconn.LocalAddr(), ifce.Name, ifce.MTU, addrs)\n\n\t\tln.conns <- conn\n\t}\n\n\treturn ln, nil\n}\n\nfunc (l *tunListener) Accept() (net.Conn, error) {\n\tselect {\n\tcase conn := <-l.conns:\n\t\treturn conn, nil\n\tcase <-l.closed:\n\t}\n\n\treturn nil, errors.New(\"accept on closed listener\")\n}\n\nfunc (l *tunListener) Addr() net.Addr {\n\treturn l.addr\n}\n\nfunc (l *tunListener) Close() error {\n\tselect {\n\tcase <-l.closed:\n\t\treturn errors.New(\"listener has been closed\")\n\tdefault:\n\t\tclose(l.closed)\n\t}\n\treturn nil\n}\n\ntype tunHandler struct {\n\toptions *HandlerOptions","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/ginuerzh/gost/blob/a33fdbf4c98034f4bfeeaea9868909822b9c526d/tuntap.go#L87-L123","documentation":"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.","triggerScenarios":"Calling Accept on a tunListener after Close(); a Close/Accept race where the closed channel is closed while Accept is blocked in the select.","commonSituations":"Graceful shutdown of TUN-based tunnels; deferred Close firing while an accept loop is still running; test teardown closing listeners before drain goroutines exit.","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"],"exampleFix":"// before\nconn, err := ln.Accept()\nif err != nil { return err }\n// after\nconn, err := ln.Accept()\nif err != nil {\n    if err.Error() == \"accept on closed listener\" { return nil }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"// guard the accept loop with a shutdown flag set before ln.Close()\nvar closing atomic.Bool","typeGuard":null,"tryCatchPattern":"conn, err := ln.Accept()\nif err != nil {\n    if strings.Contains(err.Error(), \"closed listener\") {\n        return nil // clean shutdown\n    }\n    return err\n}","preventionTips":["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"],"tags":["tun","listener","accept","closed-listener"],"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"}