router-for-me/CLIProxyAPI · error
codex.live-media-relay.public-ip is invalid: %q
Error message
codex.live-media-relay.public-ip is invalid: %q
What it means
Config validation: when the Codex live media relay is enabled, an explicitly set public-ip must parse as an IP address (net.ParseIP). This value is advertised in SDP so remote peers can reach the relay; anything unparseable is rejected at startup.
Source
Thrown at internal/config/codex_live.go:74
// EffectiveMaxSessions returns the configured media session limit.
func (c CodexLiveMediaRelayConfig) EffectiveMaxSessions() int {
if c.MaxSessions > 0 {
return c.MaxSessions
}
return DefaultCodexLiveMediaMaxSessions
}
// Validate verifies the Codex Live media relay configuration.
func (c CodexLiveMediaRelayConfig) Validate() error {
if !c.Enabled {
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)
}View on GitHub (pinned to 78f0c4079e)
Solutions
- Set public-ip to a literal IPv4/IPv6 address: public-ip: 203.0.113.5.
- If you only have a hostname, resolve it first and put the resulting IP in the config (the relay does not resolve DNS itself).
- Remove CIDR prefixes or zone identifiers; only bare addresses pass net.ParseIP.
Example fix
# before public-ip: media.example.com # after public-ip: 203.0.113.5
Defensive patterns
Strategy: validation
Validate before calling
if ip := net.ParseIP(strings.TrimSpace(cfg.PublicIP)); ip == nil {
return fmt.Errorf("public-ip %q is not a literal IP address", cfg.PublicIP)
} Type guard
func isValidPublicIP(s string) bool { return net.ParseIP(strings.TrimSpace(s)) != nil } Prevention
- DNS names are not resolved — always configure a literal IP.
- If your IP is dynamic, update the config (hot-reload supported) when it changes.
When it happens
Trigger: CodexLiveMediaRelayConfig.Validate() runs from LoadConfigOptional after YAML decode; c.PublicIP is non-empty after trimming but net.ParseIP returns nil — e.g. a hostname like 'media.example.com', or a typo like '192.168.1.256'.
Common situations: Users put a DNS name where a literal IP is required; trailing CIDR notation ('203.0.113.5/32'); trailing whitespace is trimmed but internal junk is not.
Related errors
- codex.live-media-relay UDP range requires at least %d ports
- codex.live-media-relay.max-sessions must not be negative
- codex.live-media-relay UDP port minimum and maximum must bot
- codex.live-media-relay.udp-port-min must not exceed udp-port
- codex.live-media-relay.ice-servers[%d].urls is required
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/4e7ab8f1d2253358.
Report an issue: GitHub.