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.

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.

Appendix: 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)