ginuerzh/gost · info
connection is closed
Error message
connection is closed
What it means
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.
Source
Thrown at udp.go:290
log.Logf("[udp] %s <<< %s : length %d", addr, c.LocalAddr(), n)
}
select {
case c.nopChan <- n:
default:
}
}
return
}
func (c *udpServerConn) Close() error {
c.closeMutex.Lock()
defer c.closeMutex.Unlock()
select {
case <-c.closed:
return errors.New("connection is closed")
default:
if c.config.onClose != nil {
c.config.onClose()
}
close(c.closed)
}
return nil
}
func (c *udpServerConn) ttlWait() {
ttl := c.config.ttl
if ttl == 0 {
ttl = defaultTTL
}
timer := time.NewTimer(ttl)
defer timer.Stop()
for {View on GitHub (pinned to a33fdbf4c9)
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.
Example fix
// before
defer conn.Close() // plus explicit conn.Close() on error path -> 'connection is closed'
// after
var closeOnce sync.Once
closeConn := func() { closeOnce.Do(func() { conn.Close() }) }
defer closeConn()
...
closeConn()
Defensive patterns
Strategy: try-catch
Validate before calling
var closedOnce sync.Once
func closeConn(c *udpServerConn) {
closedOnce.Do(func() { c.Close() })
} Type guard
func isConnClosedErr(err error) bool {
return err != nil && err.Error() == "connection is closed"
} Try / catch
if err := conn.Close(); err != nil && !isConnClosedErr(err) {
log.Printf("close conn: %v", err)
} Prevention
- 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.
When it happens
Trigger: Calling Close twice on a *udpServerConn — e.g. via defer plus an explicit close, or concurrent Close calls from ttlWait and application shutdown code.
Common situations: 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.
Related errors
AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02).
Data as JSON: /api/errors/e447396439f71433.
Report an issue: GitHub.