{"record":{"id":"eb249fe7f8e0c70d","repo":"tailscale/tailscale","slug":"no-address-for-node-v-v4-for-icmp","errorCode":null,"errorMessage":"no address for node %v (v4-for-icmp)","messagePattern":"no address for node (.+?) \\(v4-for-icmp\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"net/netcheck/netcheck.go","lineNumber":1283,"sourceCode":"\treturn nil\n}\n\nfunc (c *Client) measureICMPLatency(ctx context.Context, reg *tailcfg.DERPRegion, p *ping.Pinger) (_ time.Duration, ok bool, err error) {\n\tif len(reg.Nodes) == 0 {\n\t\treturn 0, false, fmt.Errorf(\"no nodes for region %d (%v)\", reg.RegionID, reg.RegionCode)\n\t}\n\n\t// Try pinging the first node in the region\n\tnode := reg.Nodes[0]\n\n\tif node.STUNPort < 0 {\n\t\t// If STUN is disabled on a node, interpret that as meaning don't measure latency.\n\t\treturn 0, false, nil\n\t}\n\tconst unusedPort = 0\n\tstunAddrPort, ok := c.nodeAddrPort(ctx, node, unusedPort, probeIPv4)\n\tif !ok {\n\t\treturn 0, false, fmt.Errorf(\"no address for node %v (v4-for-icmp)\", node.Name)\n\t}\n\tip := stunAddrPort.Addr()\n\taddr := &net.IPAddr{\n\t\tIP:   net.IP(ip.AsSlice()),\n\t\tZone: ip.Zone(),\n\t}\n\n\t// Use the unique node.Name field as the packet data to reduce the\n\t// likelihood that we get a mismatched echo response.\n\td, err := p.Send(ctx, addr, []byte(node.Name))\n\tif err != nil {\n\t\tif errors.Is(err, syscall.EPERM) {\n\t\t\treturn 0, false, nil\n\t\t}\n\t\treturn 0, false, err\n\t}\n\treturn d, true, nil\n}","sourceCodeStart":1265,"sourceCodeEnd":1301,"githubUrl":"https://github.com/tailscale/tailscale/blob/5201273aec737d6372ab7423c31c04ca3ca2a0c2/net/netcheck/netcheck.go#L1265-L1301","documentation":"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.","triggerScenarios":"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.","commonSituations":"Hand-crafted DERP maps (headscale) that omit IPv4; IPv6-only DERP deployments; typos in node address fields.","solutions":["Give every DERP node a valid IPv4 address in the map","Or disable ICMP latency probing in the netcheck client configuration","If IPv6-only DERP is intentional, treat this as per-region noise and rely on STUN/HTTPS latencies instead"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"for _, reg := range dm.Regions {\n for _, n := range reg.Nodes {\n if _, err := netip.ParseAddr(n.IPv4); err != nil {\n log.Printf(\"node %s lacks valid IPv4; ICMP latency will be skipped\", n.Name)\n }\n }\n}","typeGuard":"func nodeHasIPv4(n *tailcfg.DERPNode) bool {\n ip, err := netip.ParseAddr(n.IPv4)\n return err == nil && ip.Is4()\n}","tryCatchPattern":"dur, ok, err := c.measureICMPLatency(ctx, reg, pinger)\nif err != nil && strings.Contains(err.Error(), \"no address for node\") {\n // per-node map gap: log and fall back to other latency probes\n log.Printf(\"icmp latency unavailable: %v\", err)\n err = nil\n}","preventionTips":["Always publish an IPv4 for every DERP node in custom maps","Parse and lint node IPv4/IPv6 fields when loading a DERP map","Configure netcheck expectations: ICMP probing is best-effort, not guaranteed"],"tags":["netcheck","derp","ipv4","icmp"],"backgroundTag":"derp-map-misconfigured","analyzedSha":"5201273aec737d6372ab7423c31c04ca3ca2a0c2","analyzedAt":"2026-08-18T08:17:25.280Z","contentChangedAt":"2026-08-18T08:17:25.280Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}