grpc/grpc-go · error
error setting option on socket
Error message
error setting option on socket: %v
What it means
Fires at syscall_linux.go:85 when either rawConn.Control or the syscall.SetsockoptInt inside its callback fails while setting the TCP_USER_TIMEOUT socket option. The callback approach is required because SyscallConn hands control of the fd to the function; any setsockopt failure (bad fd, permission, EINVAL) is reported here.
Solutions
- Confirm the timeout value is non-negative and reasonable (e.g. tens of seconds); avoid values that overflow when converted to milliseconds.
- On old/embedded kernels, treat this error as non-fatal and fall back to gRPC's own keepalive/timeouts.
- Check container/seccomp policy allows setsockopt; relax the filter if it blocks IPPROTO_TCP/TCP_USER_TIMEOUT.
- Guard the call and log, since gRPC uses this only to detect dead peers faster.
Example fix
// before
if err := syscall.SetTCPUserTimeout(conn, -1*time.Second); err != nil {
return err // negative -> truncates to huge uint -> EINVAL
}
// after
if err := syscall.SetTCPUserTimeout(conn, 30*time.Second); err != nil {
log.Printf("TCP_USER_TIMEOUT unsupported on this kernel: %v", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
// Guard against absurd timeout values that truncate badly.
if timeout < 0 || timeout > math.MaxInt32*time.Millisecond {
return fmt.Errorf("refusing extreme TCP timeout %s", timeout)
} Try / catch
if err := syscall.SetTCPUserTimeout(conn, timeout); err != nil {
if errors.Is(err, syscall.ENOPROTOOPT) {
// kernel lacks TCP_USER_TIMEOUT; fall back to gRPC keepalive
log.Printf("TCP_USER_TIMEOUT unsupported: %v", err)
} else {
return err
}
} Prevention
- Use non-negative, modest timeout values (seconds, not exotic magnitudes).
- On old kernels, fall back to gRPC keepalive params.
- Check seccomp/AppArmor profiles permit setsockopt.
When it happens
Trigger: SetsockoptInt(IPPROTO_TCP, TCP_USER_TIMEOUT, ms) fails inside rawConn.Control. Causes: EBADF if the fd is invalid by the time the callback runs, EINVAL for an out-of-range timeout (negative milliseconds after timeout/time.Millisecond truncation on extreme values), or ENOPROTOOPT on kernels without TCP_USER_TIMEOUT support.
Common situations: Very old kernels (pre-2.6.37) lacking TCP_USER_TIMEOUT; passing a negative or absurdly large timeout that truncates oddly; running under a seccomp/AppArmor profile that blocks setsockopt; a race where the socket closed between SyscallConn and Control.
Related errors
- error getting raw connection
- dns: record lookup error
- malformed grpc-timeout
- transport: timeout string is too long
- transport: timeout string is too short
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/417580f69e7a9ecb.
Report an issue: GitHub.
Appendix: 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 0c51461d27)