router-for-me/CLIProxyAPI · error
codex.live-media-relay.ice-servers[%d].urls is required
Error message
codex.live-media-relay.ice-servers[%d].urls is required
What it means
Every entry under codex.live-media-relay.ice-servers must list at least one URL; an entry with an empty or missing urls list fails validation at startup. The index in the message identifies which entry (0-based) is broken.
Source
Thrown at internal/config/codex_live.go:91
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)
}
}
}
return nil
}
View on GitHub (pinned to 78f0c4079e)
Solutions
- Add urls to the offending entry: urls: [stun:stun.l.google.com:19302] or turn:... entries.
- Remove the empty ice-servers entry entirely if unused.
- Check indentation so urls nests under its list item, and verify the count of list items matches what you expect.
Example fix
# before
ice-servers:
- username: user
credential: pass
# after
ice-servers:
- urls:
- turn:turn.example.com:3478?transport=tcp
username: user
credential: pass Defensive patterns
Strategy: validation
Validate before calling
for i, s := range cfg.ICEServers {
if len(s.URLs) == 0 {
return fmt.Errorf("ice-servers[%d] has no urls", i)
}
} Prevention
- Every ice-servers list item must contain a urls list.
- Watch indentation — urls must be a sibling of username/credential under the same entry.
When it happens
Trigger: Validate loops over c.ICEServers; server.URLs is empty — e.g. an ice-servers entry containing only username/credential, or a urls: [] empty list, or a key typo like 'url:' so the field never decodes.
Common situations: Copied TURN config snippet missing the urls line; YAML indent error placing urls outside the entry; commented-out urls while leaving the stub entry.
Related errors
- codex.live-media-relay.ice-servers[%d] contains an invalid U
- codex.live-media-relay.ice-servers[%d] uses unsupported sche
- codex.live-media-relay.public-ip is invalid: %q
- codex.live-media-relay UDP range requires at least %d ports
- codex.live-media-relay.max-sessions must not be negative
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/2b5e1b794860c80f.
Report an issue: GitHub.