grpc/grpc-go · warning

error getting raw connection

Error message

error getting raw connection: %v

What it means

Fires at syscall_linux.go:79 when (*net.TCPConn).SyscallConn() returns an error inside SetTCPUserTimeout. SyscallConn yields the raw OS file descriptor so the code can call setsockopt(TCP_USER_TIMEOUT); if it fails, gRPC cannot obtain the fd to set the option and surfaces the cause. The connection itself may still be usable for data, just without the user-timeout behavior.

Solutions

  1. Check whether the conn is still open before calling SetTCPUserTimeout; ignore the error if you only set the timeout opportunistically.
  2. If you wrapped the connection, ensure the wrapper embeds/proxies *net.TCPConn so SyscallConn works.
  3. On Linux, confirm the fd is a real TCP socket; SetTCPUserTimeout is a no-op (returns nil) for non-TCP types, so a non-nil error here means the type assertion passed but SyscallConn failed.

Example fix

// before
if err := syscall.SetTCPUserTimeout(conn, 30*time.Second); err != nil {
    return err   // fatal: aborts connection setup
}

// after (treat as best-effort)
if err := syscall.SetTCPUserTimeout(conn, 30*time.Second); err != nil {
    log.Printf("could not set TCP_USER_TIMEOUT: %v", err)
}
Defensive patterns

Strategy: try-catch

Type guard

// Only attempt TCP_USER_TIMEOUT on real TCP connections.
if _, ok := conn.(*net.TCPConn); !ok {
    return // not TCP; SetTCPUserTimeout is a no-op anyway
}

Try / catch

if err := syscall.SetTCPUserTimeout(conn, timeout); err != nil {
    // SyscallConn failed; treat as best-effort, do not abort the connection
    log.Printf("SetTCPUserTimeout (getConn) skipped: %v", err)
}

Prevention

When it happens

Trigger: Calling SetTCPUserTimeout on a *net.TCPConn whose underlying fd is unavailable. Typical causes: the connection is already closed; the net.Conn was wrapped by a custom dialer that doesn't expose a real fd; running under a constrained environment (some sandboxes/seccomp filters) where fd access is blocked.

Common situations: Custom net.Dialer wrappers or connection-pool layers that return a *net.TCPConn alias over a non-TCP transport; calling SetTCPUserTimeout after the connection errored/closed; rare kernel/Gvisor environments where SyscallConn is not supported.

Related errors


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

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