ginuerzh/gost · info · net.OpError

deadline not supported

Error message

deadline not supported

What it means

http2Conn does not support deadlines: SetDeadline always returns a *net.OpError wrapping "deadline not supported". HTTP/2 multiplexed streams in this library provide no mechanism to set an overall I/O deadline on the wrapped conn, so the net.Conn deadline API is stubbed out.

Source

Thrown at http2.go:871

	if rc, ok := c.r.(io.Closer); ok {
		err = rc.Close()
	}
	if w, ok := c.w.(io.Closer); ok {
		err = w.Close()
	}
	return
}

func (c *http2Conn) LocalAddr() net.Addr {
	return c.localAddr
}

func (c *http2Conn) RemoteAddr() net.Addr {
	return c.remoteAddr
}

func (c *http2Conn) SetDeadline(t time.Time) error {
	return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

func (c *http2Conn) SetReadDeadline(t time.Time) error {
	return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

func (c *http2Conn) SetWriteDeadline(t time.Time) error {
	return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

// a dummy HTTP2 server conn used by HTTP2 handler
type http2ServerConn struct {
	r      *http.Request
	w      http.ResponseWriter
	closed chan struct{}
}

func (c *http2ServerConn) Read(b []byte) (n int, err error) {

View on GitHub (pinned to a33fdbf4c9)

Solutions

  1. Skip SetDeadline calls when the conn is an http2Conn (type-assert or feature-detect) and rely on context cancellation of the underlying HTTP/2 request instead.
  2. Implement read/write timeouts at the application layer using goroutines + timers around Read/Write.
  3. Use the h2/tls transport's context or Close() as the timeout mechanism instead of deadlines.
  4. Check err from SetDeadline and log-or-ignore if it is the unsupported-deadline OpError rather than treating it as fatal.

Example fix

// before
conn.SetDeadline(time.Now().Add(10 * time.Second)) // returns error
// after
if dc, ok := conn.(interface{ SetDeadline(time.Time) error }); ok {
    if err := dc.SetDeadline(time.Now().Add(10 * time.Second)); err != nil {
        // http2 conn: use ctx cancellation instead
        ctx, cancel = context.WithTimeout(ctx, 10*time.Second)
    }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// detect deadline support before calling
canDeadline := func(c net.Conn) bool {
    type dl interface{ SetDeadline(time.Time) error }
    _, ok := c.(dl)
    return ok // http2Conn still implements it but always errors; prefer context timeouts
}

Type guard

func supportsDeadline(c net.Conn) bool {
    var opErr *net.OpError
    err := c.SetDeadline(time.Now())
    return !(err != nil && errors.As(err, &opErr) && opErr.Net == "http2")
}

Try / catch

if err := conn.SetDeadline(t); err != nil {
    var oe *net.OpError
    if errors.As(err, &oe) && oe.Net == "http2" {
        // fall back to context-based timeout
        ctx, cancel := context.WithTimeout(ctx, d)
        defer cancel()
    } else {
        return err
    }
}

Prevention

When it happens

Trigger: Calling SetDeadline(t) on a conn obtained from an http2 listener/dialer, typically via generic net.Conn timeout code (conn.SetDeadline(time.Now().Add(5*time.Second))) or code paths like tls.Server(conn) that set handshake deadlines.

Common situations: Dropping an http2Conn into middleware that uniformly sets deadlines on all net.Conn values; wrapping in TLS or proxy code that relies on SetDeadline; porting code written for TCP conns to http2 conns.

Related errors


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