fatedier/frp · error

listen local udp addr error: %v

Error message

listen local udp addr error: %v

What it means

Returned by nathole.Prepare (pkg/nathole/nathole.go) when net.ListenUDP("udp4", laddr) fails. After resolving the local udp4 address, Prepare must actually bind a UDP socket on it for hole punching; the bind fails when the port is already taken, the address is not local, or the process lacks permission.

Source

Thrown at pkg/nathole/nathole.go:140

		return nil, fmt.Errorf("discover error: %v", err)
	}
	if len(addrs) < 2 {
		return nil, fmt.Errorf("discover error: not enough addresses")
	}

	localIPs, _ := ListLocalIPsForNatHole(10)
	natFeature, err := ClassifyNATFeature(addrs, localIPs)
	if err != nil {
		return nil, fmt.Errorf("classify nat feature error: %v", err)
	}

	laddr, err := net.ResolveUDPAddr("udp4", localAddr.String())
	if err != nil {
		return nil, fmt.Errorf("resolve local udp addr error: %v", err)
	}
	listenConn, err := net.ListenUDP("udp4", laddr)
	if err != nil {
		return nil, fmt.Errorf("listen local udp addr error: %v", err)
	}

	// Apply NAT traversal options
	var assistedAddrs []string
	if !opts.DisableAssistedAddrs {
		assistedAddrs = make([]string, 0, len(localIPs))
		for _, ip := range localIPs {
			assistedAddrs = append(assistedAddrs, net.JoinHostPort(ip, strconv.Itoa(laddr.Port)))
		}
	}
	return &PrepareResult{
		Addrs:         addrs,
		AssistedAddrs: assistedAddrs,
		ListenConn:    listenConn,
		NatType:       natFeature.NatType,
		Behavior:      natFeature.Behavior,
	}, nil
}

View on GitHub (pinned to 6c8a8d0a97)

Solutions

  1. Check the wrapped OS error: 'address already in use' means free the port or let the previous attempt close; 'cannot assign requested address' means the interface/address vanished
  2. Close ListenConn from prior PrepareResult before re-preparing
  3. Serialize xtcp preparations within one process instead of racing them
  4. In containers, ensure the network namespace permits UDP binds

Example fix

// before
res1, _ := nathole.Prepare(servers, opts)
res2, _ := nathole.Prepare(servers, opts) // may bind same local port -> fails

// after
res1, _ := nathole.Prepare(servers, opts)
// ... use res1 ...
res1.ListenConn.Close() // free the port before preparing again
res2, _ := nathole.Prepare(servers, opts)
Defensive patterns

Strategy: try-catch

Try / catch

res, err := nathole.Prepare(servers, opts)
if err != nil {
    if strings.Contains(err.Error(), "listen local udp addr error") {
        if errors.Is(err, syscall.EADDRINUSE) {
            // close prior ListenConn or wait for the port, then retry
        }
    }
}

Prevention

When it happens

Trigger: nathole.Prepare where the resolved local address:port cannot be bound — another process (or a previous, un-reaped Prepare run) already holds the port; the address no longer exists on the host (interface removed between discovery and bind); or an OS-level restriction (sandbox/container blocking bind).

Common situations: Concurrent xtcp visits on the same machine racing for the same local port; a leaked ListenConn from an earlier failed attempt; network interfaces changing (VPN up/down, DHCP renew) between discovery and listen; container security profiles denying socket creation.

Related errors


AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15). Data as JSON: /api/errors/62fcdd81e25b148f. Report an issue: GitHub.