ginuerzh/gost · error

read from closed connection

Error message

read from closed connection

What it means

udpConn.ReadFrom returns this error when the connection's `closed` channel has been signaled, meaning ReadFrom was called on an already-closed UDP connection. Any buffered packets in rChan are discarded once closed, so subsequent reads fail with this sentinel.

Source

Thrown at udp.go:249

	}
	go c.ttlWait()
	return c
}

func (c *udpServerConn) Read(b []byte) (n int, err error) {
	n, _, err = c.ReadFrom(b)
	return
}

func (c *udpServerConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
	select {
	case bb := <-c.rChan:
		n = copy(b, bb)
		if cap(bb) == mediumBufferSize {
			mPool.Put(bb[:cap(bb)])
		}
	case <-c.closed:
		err = errors.New("read from closed connection")
		return
	}

	select {
	case c.nopChan <- n:
	default:
	}

	addr = c.raddr

	return
}

func (c *udpServerConn) Write(b []byte) (n int, err error) {
	return c.WriteTo(b, c.raddr)
}

func (c *udpServerConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {

View on GitHub (pinned to a33fdbf4c9)

Solutions

  1. Exit the read loop when this error is returned — the connection is closed and will produce no more data.
  2. Cancel read goroutines (context or done channel) before or atomically with closing the connection.
  3. Treat this error as io.EOF/io.ErrClosedPipe equivalent and stop reading rather than retrying.

Example fix

// before
for {
    n, addr, err := conn.ReadFrom(buf)
    if err != nil {
        log.Println(err)
        continue // spins on closed conn
    }
}
// after
for {
    n, addr, err := conn.ReadFrom(buf)
    if err != nil {
        return // conn closed; stop reading
    }
}
Defensive patterns

Strategy: type-guard

Validate before calling

select {
case <-c.done:
    return // skip reads after shutdown signaled
default:
}

Type guard

func isConnClosedReadErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "read from closed connection")
}

Try / catch

n, addr, err := conn.ReadFrom(buf)
if err != nil {
    if isConnClosedReadErr(err) { return } // expected during shutdown
    log.Printf("read: %v", err)
}

Prevention

When it happens

Trigger: Calling Read/ReadFrom on a *udpConn after Close() has completed; a read goroutine still blocked on rChan when another goroutine closes the connection.

Common situations: Relay/pump goroutines not cancelled before connection close; closing the conn in a timeout handler while a reader is active; double-close cleanup paths.

Related errors


AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02). Data as JSON: /api/errors/0ec4126e7f4edecc. Report an issue: GitHub.