grpc/grpc-go · warning

error getting raw connection: %v

Error message

error getting raw connection: %v

What it means

This error occurs in SetTCPUserTimeout when tcpconn.SyscallConn() fails to obtain the raw file descriptor for the TCP connection. SyscallConn provides access to the underlying socket fd needed to set TCP_USER_TIMEOUT via setsockopt. Failure typically indicates the connection is in a bad state or the runtime cannot extract the fd.

Source

Thrown at internal/syscall/syscall_linux.go:79

		stimeDiffus = latest.Stime.Usec - first.Stime.Usec
	)

	uTimeElapsed := float64(utimeDiffs) + float64(utimeDiffus)*1.0e-6
	sTimeElapsed := float64(stimeDiffs) + float64(stimeDiffus)*1.0e-6

	return uTimeElapsed, sTimeElapsed
}

// SetTCPUserTimeout sets the TCP user timeout on a connection's socket
func SetTCPUserTimeout(conn net.Conn, timeout time.Duration) error {
	tcpconn, ok := conn.(*net.TCPConn)
	if !ok {
		// not a TCP connection. exit early
		return nil
	}
	rawConn, err := tcpconn.SyscallConn()
	if err != nil {
		return fmt.Errorf("error getting raw connection: %v", err)
	}
	err = rawConn.Control(func(fd uintptr) {
		err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_USER_TIMEOUT, int(timeout/time.Millisecond))
	})
	if err != nil {
		return fmt.Errorf("error setting option on socket: %v", err)
	}

	return nil
}

// GetTCPUserTimeout gets the TCP user timeout on a connection's socket
func GetTCPUserTimeout(conn net.Conn) (opt int, err error) {
	tcpconn, ok := conn.(*net.TCPConn)
	if !ok {
		err = fmt.Errorf("conn is not *net.TCPConn. got %T", conn)
		return
	}

View on GitHub (pinned to 03255a9237)

Solutions

  1. Check for concurrent close/teardown of the connection that races with keepalive setup.
  2. Ensure the net.Conn passed to gRPC is a standard *net.TCPConn, not a wrapper that breaks SyscallConn.
  3. If using a custom dialer, verify it returns real TCP connections.
  4. On non-critical errors, the transport setup will fail — review the transport logs for the root cause.

Example fix

// before (broken): custom dialer wraps TCPConn, breaking SyscallConn
dialer := func(ctx context.Context, addr string) (net.Conn, error) {
    c, err := net.Dial("tcp", addr)
    return &myWrapper{c}, err // SyscallConn() broken
}

// after (valid): return the raw *net.TCPConn
dialer := func(ctx context.Context, addr string) (net.Conn, error) {
    return net.Dial("tcp", addr) // returns *net.TCPConn, SyscallConn works
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify the connection is a healthy *net.TCPConn before setting timeout
func safeSetTCPUserTimeout(conn net.Conn, timeout time.Duration) error {
    tcp, ok := conn.(*net.TCPConn)
    if !ok { return nil // not TCP, skip silently like SetTCPUserTimeout does
    }
    if tcp.RemoteAddr() == nil { return fmt.Errorf("connection has no remote addr") }
    return syscall.SetTCPUserTimeout(conn, timeout)
}

Type guard

func isTCPConn(conn net.Conn) bool {
    _, ok := conn.(*net.TCPConn)
    return ok
}

Try / catch

if err := syscall.SetTCPUserTimeout(conn, kp.Timeout); err != nil {
    if strings.Contains(err.Error(), "raw connection") {
        // connection may be closing; non-fatal in some paths
        log.Printf("warning: could not set TCP_USER_TIMEOUT: %v", err)
    }
}

Prevention

When it happens

Trigger: During gRPC server or client transport setup, SetTCPUserTimeout is called on a *net.TCPConn and SyscallConn() returns an error. This can happen if the connection's file descriptor has already been closed or put into an unusable state by the Go runtime.

Common situations: Connection closed concurrently (race between transport setup and teardown), Go runtime netpoller issues, or the connection is backed by a non-standard net.Conn wrapper that doesn't properly implement SyscallConn.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/3f56c5a914b4e3b4. Report an issue: GitHub.