cloudflare/cloudflared · warning

error setting read deadline: %w

Error message

error setting read deadline: %w

What it means

GorillaConn.SetDeadline implements net.Conn by delegating to the underlying gorilla websocket connection. This error wraps a failure from SetReadDeadline, which happens when the underlying connection's deadline setter reports the connection is already closed or in an unusable state.

Source

Thrown at websocket/connection.go:73

	return copied, nil
}

// Write will write messages to the websocket connection
func (c *GorillaConn) Write(p []byte) (int, error) {
	if err := c.Conn.WriteMessage(websocket.BinaryMessage, p); err != nil {
		return 0, err
	}

	return len(p), nil
}

// SetDeadline sets both read and write deadlines, as per net.Conn interface docs:
// "It is equivalent to calling both SetReadDeadline and SetWriteDeadline."
// Note there is no synchronization here, but the gorilla implementation isn't thread safe anyway
func (c *GorillaConn) SetDeadline(t time.Time) error {
	if err := c.Conn.SetReadDeadline(t); err != nil {
		return fmt.Errorf("error setting read deadline: %w", err)
	}
	if err := c.Conn.SetWriteDeadline(t); err != nil {
		return fmt.Errorf("error setting write deadline: %w", err)
	}
	return nil
}

type Conn struct {
	rw  io.ReadWriter
	log *zerolog.Logger
	// writeLock makes sure
	// 1. Only one write at a time. The pinger and Stream function can both call write.
	// 2. Close only returns after in progress Write is finished, and no more Write will succeed after calling Close.
	writeLock sync.Mutex
	done      bool
}

func NewConn(ctx context.Context, rw io.ReadWriter, log *zerolog.Logger) *Conn {

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Check whether the connection was already closed — stop deadline timers on connection shutdown
  2. Ignore/treat as benign errors of type net.ErrClosed or use-after-close in deadline reset paths
  3. Ensure Close() cancels the goroutines/timers that call SetDeadline before closing
  4. If gorilla returns 'use of closed network connection', rebuild the connection rather than retrying the deadline

Example fix

// before
if err := conn.SetDeadline(time.Now().Add(pongWait)); err != nil {
    return err // aborts even during shutdown
}
// after
if err := conn.SetDeadline(time.Now().Add(pongWait)); err != nil {
    if errors.Is(err, net.ErrClosed) {
        return nil // connection is shutting down
    }
    return fmt.Errorf("set deadline: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

func canSetDeadline(c net.Conn) bool {
    return c.SetDeadline(time.Now()) == nil || !errors.Is(c.SetDeadline(time.Now()), net.ErrClosed)
}

Try / catch

if err := conn.SetDeadline(t); err != nil {
    if errors.Is(err, net.ErrClosed) || strings.Contains(err.Error(), "use of closed") {
        return nil // expected during shutdown
    }
    return err
}

Prevention

When it happens

Trigger: Calling SetDeadline (directly or via net.Conn plumbing like conn.SetDeadline in proxy/ssh/rdp over websocket code) after the websocket has been closed or the underlying TCP conn has errored.

Common situations: Deadline refresh timers racing connection shutdown; one side closed the tunnel while keepalive/ping loops still reset deadlines; use-after-close in proxy streams.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/71e96bb0528360b4. Report an issue: GitHub.