prometheus/node_exporter · error

couldn't get tcpstats

Error message

couldn't get tcpstats: %w

What it means

tcpStatCollector.Update gathers IPv4 TCP connection states via getTCPStats(syscall.AF_INET), which uses a netlink INET_DIAG socket. When that fails, the error is wrapped as "couldn't get tcpstats". The netlink error is preserved via %w for errors.Is/As inspection.

Solutions

  1. Check the process can create NETLINK_INET_DIAG sockets (test with `ss -t` which uses the same mechanism)
  2. Grant required capabilities in containers (e.g. docker run --cap-add=NET_ADMIN) or relax seccomp
  3. If IPv4 stats fail persistently, disable the collector with --collector.tcpstat
  4. Confirm the kernel supports inet_diag (CONFIG_INET_DIAG) — common in custom/minimal kernels

Example fix

// before
docker run prom/node-exporter --collector.tcpstat
// after
docker run --cap-add=NET_ADMIN prom/node-exporter --collector.tcpstat
Defensive patterns

Strategy: fallback

Validate before calling

if _, err := os.Stat("/proc/net/tcp"); err != nil {
    // /proc unavailable — tcpstat cannot work
}

Try / catch

if err := coll.Update(ch); err != nil {
    if errors.Is(err, os.ErrPermission) {
        log.Warn("tcpstat lacks netlink permission; disabling")
    } else {
        log.Warn("tcpstat scrape failed", "err", err)
    }
}

Prevention

When it happens

Trigger: The initial getTCPStats(AF_INET) call returns an error: netlink socket creation fails, the sock_diag request fails, or the kernel reply cannot be parsed into connection stats.

Common situations: Hardened containers lacking CAP_NET_ADMIN/CAP_SYS_PTRACE where inet_diag requests are denied; seccomp profiles blocking netlink socket creation; reading /proc/net/tcp disabled in the environment (needed for availability checks); very old or patched kernels lacking sock_diag for the requested family.

Related errors


AI-assisted analysis of prometheus/node_exporter@17ddd77c59 (2026-09-07). Data as JSON: /api/errors/42f0220f464fa206. Report an issue: GitHub.

Appendix: source

Thrown at collector/tcpstat_linux.go:133

	State   uint8
	Timer   uint8
	Retrans uint8
	ID      InetDiagSockID
	Expires uint32
	RQueue  uint32
	WQueue  uint32
	UID     uint32
	Inode   uint32
}

func parseInetDiagMsg(b []byte) *InetDiagMsg {
	return (*InetDiagMsg)(unsafe.Pointer(&b[0]))
}

func (c *tcpStatCollector) Update(ch chan<- prometheus.Metric) error {
	tcpStats, err := getTCPStats(syscall.AF_INET)
	if err != nil {
		return fmt.Errorf("couldn't get tcpstats: %w", err)
	}

	// if enabled ipv6 system
	if _, hasIPv6 := os.Stat(procFilePath("net/tcp6")); hasIPv6 == nil {
		tcp6Stats, err := getTCPStats(syscall.AF_INET6)
		if err != nil {
			return fmt.Errorf("couldn't get tcp6stats: %w", err)
		}

		for st, value := range tcp6Stats {
			tcpStats[st] += value
		}
	}

	for st, value := range tcpStats {
		ch <- c.desc.mustNewConstMetric(value, st.String())
	}

View on GitHub (pinned to 17ddd77c59)