ginuerzh/gost · info · net.OpError

deadline not supported

Error message

deadline not supported

What it means

dnsServerConn.SetDeadline unconditionally returns a *net.OpError wrapping 'deadline not supported' because DNS-over-HTTP/UDP virtual connections have no real socket deadlines to enforce. This is a deliberate capability limitation of the emulated conn, satisfying the net.Conn interface without implementing deadlines.

Source

Thrown at dns.go:391

func (c *dnsServerConn) Close() error {
	select {
	case <-c.cclose:
	default:
		close(c.cclose)
	}
	return nil
}

func (c *dnsServerConn) LocalAddr() net.Addr {
	return c.laddr
}

func (c *dnsServerConn) RemoteAddr() net.Addr {
	return c.raddr
}

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

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

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

type dnsResponseWriter interface {
	io.Writer
	RemoteAddr() net.Addr
}

type dohResponseWriter struct {
	raddr net.Addr
	http.ResponseWriter

View on GitHub (pinned to a33fdbf4c9)

Solutions

  1. Skip SetDeadline calls for DNS conns or guard them behind an interface check
  2. Use context-based timeouts in the handler instead of conn deadlines
  3. Ignore the error if deadlines are non-essential for the DNS exchange
  4. Implement a real deadline mechanism (time.AfterFunc closing conn) if needed

Example fix

// before
conn.SetDeadline(time.Now().Add(10 * time.Second))
// after
type deadliner interface{ SetDeadline(time.Time) error }
if d, ok := conn.(deadliner); ok {
    if err := d.SetDeadline(time.Now().Add(10 * time.Second)); err != nil {
        log.Logf("deadline unsupported: %s", err) // DNS conn: use context timeout instead
    }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// detect deadline support before relying on it
type deadliner interface{ SetDeadline(time.Time) error }
_, supportsDeadline := conn.(interface{ SupportsDeadline() bool }) // DNS conns do not

Type guard

var _ net.Conn = (*dnsServerConn)(nil) // known: deadlines unsupported
func deadlinesSupported(c net.Conn) bool {
    _, isDNSConn := c.(interface{ isDNSServerConnMarker() })
    return !isDNSConn
}

Try / catch

if err := conn.SetDeadline(time.Now().Add(d)); err != nil {
    var opErr *net.OpError
    if errors.As(err, &opErr) && opErr.Err.Error() == "deadline not supported" {
        // fall back to context-based timeout
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Calling SetDeadline(t) on a conn obtained from the DNS listener's Accept(); any non-zero-time call immediately fails with this wrapped error.

Common situations: Wrapping the DNS conn in code that sets deadlines by default (e.g., io.LimitReader with timeout helpers, TLS handshake with deadline, copy loops with timeouts).

Related errors


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