grpc/grpc-go · info

conn is not *net.TCPConn. got %T

Error message

conn is not *net.TCPConn. got %T

What it means

This error occurs in GetTCPUserTimeout when the provided net.Conn is not a *net.TCPConn. Unlike SetTCPUserTimeout (which silently returns nil for non-TCP conns), GetTCPUserTimeout explicitly errors. This is used to read the current TCP_USER_TIMEOUT value, typically for diagnostics/channelz.

Source

Thrown at internal/syscall/syscall_linux.go:95

	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) {
		opt, err = syscall.GetsockoptInt(int(fd), syscall.IPPROTO_TCP, unix.TCP_USER_TIMEOUT)
	})
	if err != nil {
		err = fmt.Errorf("error getting option on socket: %v", err)
		return
	}

	return
}

View on GitHub (pinned to 03255a9237)

Solutions

  1. If using Unix domain sockets, this error is expected and benign — TCP options don't apply.
  2. If you expect a TCP connection, verify the listener/dialer configuration isn't using a non-TCP transport.
  3. Suppress or ignore this error for non-TCP transports in diagnostic code paths.

Example fix

// before (broken): expecting TCP keepalive on a unix socket connection
lis, _ := net.Listen("unix", "/tmp/grpc.sock")
// GetTCPUserTimeout(conn) → "conn is not *net.TCPConn. got *net.UnixConn"

// after (valid): check connection type before calling
if _, ok := conn.(*net.TCPConn); ok {
    timeout, err := syscall.GetTCPUserTimeout(conn)
    // use timeout...
} else {
    // non-TCP connection; TCP options not applicable
}
Defensive patterns

Strategy: type-guard

Type guard

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

// Usage:
// if tcp, ok := isTCPConn(conn); ok {
//     timeout, err := syscall.GetTCPUserTimeout(tcp)
// }

Try / catch

if _, err := syscall.GetTCPUserTimeout(conn); err != nil {
    if strings.Contains(err.Error(), "not *net.TCPConn") {
        // expected for unix sockets or non-TCP transports; ignore
        return
    }
    log.Printf("error reading TCP_USER_TIMEOUT: %v", err)
}

Prevention

When it happens

Trigger: GetTCPUserTimeout is called with a net.Conn that is not *net.TCPConn — e.g., a unix socket connection, a TLS connection wrapper, or a custom net.Conn type. The type assertion fails and the error reports the actual concrete type.

Common situations: gRPC server configured with a Unix domain socket listener (unix:///path) where channelz or diagnostic code attempts to read TCP keepalive settings, or a custom transport that provides a non-TCP underlying connection.

Related errors


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