{"record":{"id":"e6a81565cbed4c97","repo":"ginuerzh/gost","slug":"closed","errorCode":null,"errorMessage":"closed","messagePattern":"closed","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"info","filePath":"forward.go","lineNumber":446,"sourceCode":"\n\t\tselect {\n\t\tcase l.connChan <- conn:\n\t\tdefault:\n\t\t\tconn.Close()\n\t\t\tlog.Logf(\"[rtcp] %s - %s: connection queue is full\", conn.RemoteAddr(), conn.LocalAddr())\n\t\t}\n\t}\n}\n\nfunc (l *tcpRemoteForwardListener) Accept() (conn net.Conn, err error) {\n\tif l.ln != nil {\n\t\treturn l.ln.Accept()\n\t}\n\n\tselect {\n\tcase conn = <-l.connChan:\n\tcase <-l.closed:\n\t\terr = errors.New(\"closed\")\n\t}\n\n\treturn\n}\n\nfunc (l *tcpRemoteForwardListener) accept() (conn net.Conn, err error) {\n\tlastNode := l.chain.LastNode()\n\tif lastNode.Protocol == \"forward\" && lastNode.Transport == \"ssh\" {\n\t\treturn l.chain.Dial(l.addr.String())\n\t}\n\n\tif l.isChainValid() {\n\t\tif lastNode.GetBool(\"mbind\") {\n\t\t\treturn l.muxAccept() // multiplexing support for binding.\n\t\t}\n\n\t\tcc, er := l.chain.Conn()\n\t\tif er != nil {","sourceCodeStart":428,"sourceCodeEnd":464,"githubUrl":"https://github.com/ginuerzh/gost/blob/a33fdbf4c98034f4bfeeaea9868909822b9c526d/forward.go#L428-L464","documentation":"tcpRemoteForwardListener.Accept returns 'closed' when the listener's closed channel has been fired, i.e. Accept was called after Close(). This is the standard net.Listener convention signaling the listener is shutting down and no more connections will be delivered on connChan.","triggerScenarios":"Calling Accept on a remote-forward TCP listener after Close() was invoked, or racing Close with Accept so the select falls into the <-l.closed branch.","commonSituations":"Graceful shutdown of a remote port-forwarding server (gost rtcp), Stop/reload of services, test teardown ordering issues.","solutions":["Stop the Accept loop when this error is returned (standard shutdown path)","Synchronize shutdown: close listener only after Accept loop exits or accept the benign error","Compare error message/type to 'closed' and return io.EOF/ErrListenerClosed semantics upward","Avoid logging it as fatal during shutdown"],"exampleFix":"// before\nfor {\n    conn, err := l.Accept()\n    if err != nil {\n        log.Fatal(err)\n    }\n    go handle(conn)\n}\n// after\nfor {\n    conn, err := l.Accept()\n    if err != nil {\n        if err.Error() == \"closed\" {\n            return // listener shut down; exit loop cleanly\n        }\n        log.Log(err)\n        continue\n    }\n    go handle(conn)\n}","handlingStrategy":"try-catch","validationCode":"// pre-check listener state before entering the accept loop\nselect {\ncase <-l.closed:\n    return // already closed; skip accept loop entirely\ndefault:\n}","typeGuard":"func isListenerClosed(err error) bool {\n    return err != nil && err.Error() == \"closed\"\n}","tryCatchPattern":"conn, err := l.Accept()\nif err != nil {\n    if isListenerClosed(err) {\n        return nil // graceful shutdown: stop accepting\n    }\n    // handle transient error with tempDelay backoff\n    return err\n}","preventionTips":["Exit accept loops immediately on this error rather than retrying","Serialize Close() and Accept() shutdown ordering in one shutdown path","Normalize the error to io.ErrClosedPipe/ErrListenerClosed in wrappers","Never log 'closed' as fatal during teardown"],"tags":["listener","go","shutdown","tcp-forward"],"backgroundTag":"listener-closed","analyzedSha":"a33fdbf4c98034f4bfeeaea9868909822b9c526d","analyzedAt":"2026-09-02T22:15:54.506Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}