router-for-me/CLIProxyAPI · error

codex.live-media-relay UDP range requires at least %d ports

Error message

codex.live-media-relay UDP range requires at least %d ports for %d sessions

What it means

Each media session consumes 2 UDP ports in the configured range (one per side of the relayed flow), so Validate requires at least EffectiveMaxSessions()*2 ports between udp-port-min and udp-port-max. With the default 32 sessions that is 64 ports; a narrower range fails startup with this error.

Source

Thrown at internal/config/codex_live.go:86

		return nil
	}
	if c.MaxSessions < 0 {
		return errors.New("codex.live-media-relay.max-sessions must not be negative")
	}
	if publicIP := strings.TrimSpace(c.PublicIP); publicIP != "" && net.ParseIP(publicIP) == nil {
		return fmt.Errorf("codex.live-media-relay.public-ip is invalid: %q", publicIP)
	}
	if (c.UDPPortMin == 0) != (c.UDPPortMax == 0) {
		return errors.New("codex.live-media-relay UDP port minimum and maximum must both be set")
	}
	if c.UDPPortMin > c.UDPPortMax {
		return errors.New("codex.live-media-relay.udp-port-min must not exceed udp-port-max")
	}
	if c.UDPPortMin != 0 {
		availablePorts := int(c.UDPPortMax) - int(c.UDPPortMin) + 1
		requiredPorts := c.EffectiveMaxSessions() * 2
		if availablePorts < requiredPorts {
			return fmt.Errorf("codex.live-media-relay UDP range requires at least %d ports for %d sessions", requiredPorts, c.EffectiveMaxSessions())
		}
	}
	for serverIndex, server := range c.ICEServers {
		if len(server.URLs) == 0 {
			return fmt.Errorf("codex.live-media-relay.ice-servers[%d].urls is required", serverIndex)
		}
		for _, rawURL := range server.URLs {
			parsed, errParse := url.Parse(strings.TrimSpace(rawURL))
			if errParse != nil || parsed.Scheme == "" {
				return fmt.Errorf("codex.live-media-relay.ice-servers[%d] contains an invalid URL", serverIndex)
			}
			switch strings.ToLower(parsed.Scheme) {
			case "stun", "stuns", "turn", "turns":
			default:
				return fmt.Errorf("codex.live-media-relay.ice-servers[%d] uses unsupported scheme %q", serverIndex, parsed.Scheme)
			}
		}
	}

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Widen the range to at least 2*maxSessions ports: for 32 sessions use >=64 ports (e.g. 50000-50099).
  2. Or lower max-sessions so 2*sessions fits the current range.
  3. Or leave both port bounds unset (0) to use the default unbounded behavior.

Example fix

# before (21 ports < 64 needed for 32 sessions)
udp-port-min: 50000
udp-port-max: 50020

# after
udp-port-min: 50000
udp-port-max: 50099
Defensive patterns

Strategy: validation

Validate before calling

ports := cfg.UDPPortMax - cfg.UDPPortMin + 1
if cfg.UDPPortMin != 0 && ports < cfg.EffectiveMaxSessions()*2 {
	return fmt.Errorf("need >=%d UDP ports, have %d", cfg.EffectiveMaxSessions()*2, ports)
}

Prevention

When it happens

Trigger: udp-port-min/udp-port-max are both non-zero, min<=max, but (max-min+1) < maxSessions*2 — e.g. min 50000, max 50020 (21 ports) with the default 32 sessions.

Common situations: Operators narrow the UDP range for firewall rules and forget the 2-ports-per-session requirement; raising max-sessions without widening the range.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/d1e12c784ad4dddb. Report an issue: GitHub.