grpc/grpc-go · warning

error setting option on socket: %v

Error message

error setting option on socket: %v

What it means

This error occurs in SetTCPUserTimeout when the rawConn.Control callback or syscall.SetsockoptInt fails to set the TCP_USER_TIMEOUT socket option. This option (Linux-specific) tells the kernel how long to wait for an ACK before aborting the connection. Failure indicates a kernel or socket-level problem.

Source

Thrown at internal/syscall/syscall_linux.go:85

	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
	}
	rawConn, err := tcpconn.SyscallConn()
	if err != nil {
		err = fmt.Errorf("error getting raw connection: %v", err)
		return
	}
	err = rawConn.Control(func(fd uintptr) {

View on GitHub (pinned to 03255a9237)

Solutions

  1. Verify the OS/kernel supports TCP_USER_TIMEOUT (Linux >= 2.6.37).
  2. Check for connection races where the fd is closed before the setsockopt call completes.
  3. If on a minimal container/VM, ensure the kernel is recent enough.
  4. This error is fatal to transport creation — review whether a custom keepalive timeout is necessary and falls back if the kernel doesn't support it.

Example fix

// No direct code fix — this is a kernel/environment capability issue.
// Verify kernel support:
//   uname -r  # ensure >= 2.6.37
//   # check the option exists:
//   grep TCP_USER_TIMEOUT /usr/include/netinet/tcp.h

// If unsupported, avoid setting keepalive params that trigger SetTCPUserTimeout:
// Use default keepalive (Time == infinity) or ensure your runtime supports the option.
Defensive patterns

Strategy: try-catch

Validate before calling

// Check kernel support for TCP_USER_TIMEOUT before relying on it
func supportsTCPUserTimeout() bool {
    // TCP_USER_TIMEOUT is available on Linux >= 2.6.37
    if runtime.GOOS != "linux" { return false }
    // Optionally probe with a temporary socket
    fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
    if err != nil { return false }
    defer syscall.Close(fd)
    return syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, unix.TCP_USER_TIMEOUT, 1000) == nil
}

Try / catch

if err := syscall.SetTCPUserTimeout(conn, timeout); err != nil {
    if strings.Contains(err.Error(), "setting option on socket") {
        // kernel may not support TCP_USER_TIMEOUT
        // proceed without it; keepalive still works at the gRPC level
        log.Printf("warning: TCP_USER_TIMEOUT unsupported: %v", err)
    }
}

Prevention

When it happens

Trigger: During transport setup, after obtaining the raw fd, syscall.SetsockoptInt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, value) returns a non-nil error. This could be an EINVAL from an invalid timeout value, ENOPROTOOPT on kernels that don't support TCP_USER_TIMEOUT, or EBADF if the fd is stale.

Common situations: Running on an older Linux kernel (< 2.6.37) that doesn't support TCP_USER_TIMEOUT, running on a non-Linux OS via a compatibility layer that rejects the option, or a race condition where the fd was closed before setsockopt executed.

Related errors


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