{"record":{"id":"e447396439f71433","repo":"ginuerzh/gost","slug":"connection-is-closed-e44739","errorCode":null,"errorMessage":"connection is closed","messagePattern":"connection is closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"udp.go","lineNumber":290,"sourceCode":"\t\t\tlog.Logf(\"[udp] %s <<< %s : length %d\", addr, c.LocalAddr(), n)\n\t\t}\n\n\t\tselect {\n\t\tcase c.nopChan <- n:\n\t\tdefault:\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (c *udpServerConn) Close() error {\n\tc.closeMutex.Lock()\n\tdefer c.closeMutex.Unlock()\n\n\tselect {\n\tcase <-c.closed:\n\t\treturn errors.New(\"connection is closed\")\n\tdefault:\n\t\tif c.config.onClose != nil {\n\t\t\tc.config.onClose()\n\t\t}\n\t\tclose(c.closed)\n\t}\n\treturn nil\n}\n\nfunc (c *udpServerConn) ttlWait() {\n\tttl := c.config.ttl\n\tif ttl == 0 {\n\t\tttl = defaultTTL\n\t}\n\ttimer := time.NewTimer(ttl)\n\tdefer timer.Stop()\n\n\tfor {","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/ginuerzh/gost/blob/a33fdbf4c98034f4bfeeaea9868909822b9c526d/udp.go#L272-L308","documentation":"udpServerConn.Close returns this error when the connection has already been closed. A `closed` channel guarded by a mutex makes Close non-idempotent: a second call finds the channel closed and returns this sentinel instead of re-running cleanup.","triggerScenarios":"Calling Close twice on a *udpServerConn — e.g. via defer plus an explicit close, or concurrent Close calls from ttlWait and application shutdown code.","commonSituations":"Idle-TTL cleanup (ttlWait) closing the connection while the user code also closes it; error-handling paths that close the conn and then a deferred Close fires again.","solutions":["Close the connection exactly once; consolidate cleanup into a single path (e.g. sync.Once).","Ignore this error in deferred Close calls — double close performs no action and is harmless.","If Close races with ttlWait, prefer relying on the library's TTL close and skip the manual close, or remove the ttl configured."],"exampleFix":"// before\ndefer conn.Close() // plus explicit conn.Close() on error path -> 'connection is closed'\n// after\nvar closeOnce sync.Once\ncloseConn := func() { closeOnce.Do(func() { conn.Close() }) }\ndefer closeConn()\n...\ncloseConn()\n","handlingStrategy":"try-catch","validationCode":"var closedOnce sync.Once\nfunc closeConn(c *udpServerConn) {\n    closedOnce.Do(func() { c.Close() })\n}","typeGuard":"func isConnClosedErr(err error) bool {\n    return err != nil && err.Error() == \"connection is closed\"\n}","tryCatchPattern":"if err := conn.Close(); err != nil && !isConnClosedErr(err) {\n    log.Printf(\"close conn: %v\", err)\n}","preventionTips":["Wrap Close in sync.Once so multiple callers are safe.","Ignore errors from deferred Close calls.","Be aware ttlWait may close idle conns — don't double-close after TTL shutdown.","Coordinate application shutdown with the library's TTL cleanup."],"tags":["udp","connection","double-close","network"],"backgroundTag":"connection-already-closed","analyzedSha":"a33fdbf4c98034f4bfeeaea9868909822b9c526d","analyzedAt":"2026-09-02T22:15:54.506Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}