{"record":{"id":"7a54cc66fd679db7","repo":"fatedier/frp","slug":"resolve-local-udp-addr-error-v","errorCode":null,"errorMessage":"resolve local udp addr error: %v","messagePattern":"resolve local udp addr error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/nathole/nathole.go","lineNumber":136,"sourceCode":"func Prepare(stunServers []string, opts PrepareOptions) (*PrepareResult, error) {\n\t// discover for Nat type\n\taddrs, localAddr, err := Discover(stunServers, \"\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"discover error: %v\", err)\n\t}\n\tif len(addrs) < 2 {\n\t\treturn nil, fmt.Errorf(\"discover error: not enough addresses\")\n\t}\n\n\tlocalIPs, _ := ListLocalIPsForNatHole(10)\n\tnatFeature, err := ClassifyNATFeature(addrs, localIPs)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"classify nat feature error: %v\", err)\n\t}\n\n\tladdr, err := net.ResolveUDPAddr(\"udp4\", localAddr.String())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"resolve local udp addr error: %v\", err)\n\t}\n\tlistenConn, err := net.ListenUDP(\"udp4\", laddr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"listen local udp addr error: %v\", err)\n\t}\n\n\t// Apply NAT traversal options\n\tvar assistedAddrs []string\n\tif !opts.DisableAssistedAddrs {\n\t\tassistedAddrs = make([]string, 0, len(localIPs))\n\t\tfor _, ip := range localIPs {\n\t\t\tassistedAddrs = append(assistedAddrs, net.JoinHostPort(ip, strconv.Itoa(laddr.Port)))\n\t\t}\n\t}\n\treturn &PrepareResult{\n\t\tAddrs:         addrs,\n\t\tAssistedAddrs: assistedAddrs,\n\t\tListenConn:    listenConn,","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/nathole/nathole.go#L118-L154","documentation":"Returned by nathole.Prepare (pkg/nathole/nathole.go) when net.ResolveUDPAddr(\"udp4\", localAddr.String()) fails. Prepare re-resolves the local address discovered during STUN binding as an explicit udp4 address before listening on it; failure means the discovered local address string cannot be parsed as an IPv4 UDP endpoint.","triggerScenarios":"nathole.Prepare where localAddr (the local socket address from discovery) stringifies into something unresolvable as udp4 — e.g. an IPv6 local address on a dual-stack socket, since the network is hard-coded to \"udp4\" here.","commonSituations":"Host whose STUN exchange ran over IPv6 (STUN server resolved to an AAAA record) so the local endpoint is IPv6 while Prepare demands udp4; unusual local address formats from the OS.","solutions":["Force IPv4 for STUN discovery — use IPv4 literals or hostnames that resolve A records for the STUN servers","Disable IPv6 on the socket/host if the environment prefers v6 unexpectedly","Check the wrapped error text; anything other than address-family mismatch is a genuine malformed address and worth reporting"],"exampleFix":"# before\nnatHoleSTUNServer = [\"stun.example.com:3478\"]  # may resolve to IPv6\n\n# after\nnatHoleSTUNServer = [\"203.0.113.20:3478\"]  # IPv4 literal forces udp4 path","handlingStrategy":"validation","validationCode":"if localAddr != nil && localAddr.IP.To4() == nil {\n    // discovery happened over IPv6; force IPv4 STUN endpoints before Prepare\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"resolve local udp addr error\") {\n    // switch STUN config to IPv4 literals/endpoints and retry Prepare\n}","preventionTips":["Use IPv4 STUN server addresses for xtcp","Check host DNS resolution prefers A records for STUN hostnames","Disable IPv6 for the frpc socket if the environment is dual-stack"],"tags":["go","frp","nathole","ipv6","network"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}