tailscale/tailscale · warning

no address for node %v (v4-for-icmp)

Error message

no address for node %v (v4-for-icmp)

What it means

For ICMP latency probing, netcheck needs an IPv4 address for the DERP node (nodeAddrPort with probeIPv4). If the node's IPv4 field is empty or unparseable and no substitute address can be derived, the ICMP measurement for that region fails with the node name included.

Source

Thrown at net/netcheck/netcheck.go:1283

	return nil
}

func (c *Client) measureICMPLatency(ctx context.Context, reg *tailcfg.DERPRegion, p *ping.Pinger) (_ time.Duration, ok bool, err error) {
	if len(reg.Nodes) == 0 {
		return 0, false, fmt.Errorf("no nodes for region %d (%v)", reg.RegionID, reg.RegionCode)
	}

	// Try pinging the first node in the region
	node := reg.Nodes[0]

	if node.STUNPort < 0 {
		// If STUN is disabled on a node, interpret that as meaning don't measure latency.
		return 0, false, nil
	}
	const unusedPort = 0
	stunAddrPort, ok := c.nodeAddrPort(ctx, node, unusedPort, probeIPv4)
	if !ok {
		return 0, false, fmt.Errorf("no address for node %v (v4-for-icmp)", node.Name)
	}
	ip := stunAddrPort.Addr()
	addr := &net.IPAddr{
		IP:   net.IP(ip.AsSlice()),
		Zone: ip.Zone(),
	}

	// Use the unique node.Name field as the packet data to reduce the
	// likelihood that we get a mismatched echo response.
	d, err := p.Send(ctx, addr, []byte(node.Name))
	if err != nil {
		if errors.Is(err, syscall.EPERM) {
			return 0, false, nil
		}
		return 0, false, err
	}
	return d, true, nil
}

View on GitHub (pinned to 5201273aec)

Solutions

  1. Give every DERP node a valid IPv4 address in the map
  2. Or disable ICMP latency probing in the netcheck client configuration
  3. If IPv6-only DERP is intentional, treat this as per-region noise and rely on STUN/HTTPS latencies instead
Defensive patterns

Strategy: validation

Validate before calling

for _, reg := range dm.Regions {
 for _, n := range reg.Nodes {
 if _, err := netip.ParseAddr(n.IPv4); err != nil {
 log.Printf("node %s lacks valid IPv4; ICMP latency will be skipped", n.Name)
 }
 }
}

Type guard

func nodeHasIPv4(n *tailcfg.DERPNode) bool {
 ip, err := netip.ParseAddr(n.IPv4)
 return err == nil && ip.Is4()
}

Try / catch

dur, ok, err := c.measureICMPLatency(ctx, reg, pinger)
if err != nil && strings.Contains(err.Error(), "no address for node") {
 // per-node map gap: log and fall back to other latency probes
 log.Printf("icmp latency unavailable: %v", err)
 err = nil
}

Prevention

When it happens

Trigger: A DERP map node with only an IPv6 address, a typo'd IPv4 (e.g. '1.2.3.256'), or an empty IPv4 field while ICMP latency measurement is enabled.

Common situations: Hand-crafted DERP maps (headscale) that omit IPv4; IPv6-only DERP deployments; typos in node address fields.

Related errors


AI-assisted analysis of tailscale/tailscale@5201273aec (2026-08-18). Data as JSON: /api/errors/eb249fe7f8e0c70d. Report an issue: GitHub.