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
- Check whether the conn is still open before calling SetTCPUserTimeout; ignore the error if you only set the timeout opportunistically.
- If you wrapped the connection, ensure the wrapper embeds/proxies *net.TCPConn so SyscallConn works.
- 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
- Treat SetTCPUserTimeout as opportunistic; log and continue on failure.
- Ensure custom dialers return real *net.TCPConn values.
- Don't call it on an already-closed connection.
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
- error setting option on socket
- 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/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)