{"record":{"id":"e1554352e68c434d","repo":"router-for-me/CLIProxyAPI","slug":"codex-live-media-relay-ice-servers-d-uses-unsupp","errorCode":null,"errorMessage":"codex.live-media-relay.ice-servers[%d] uses unsupported scheme %q","messagePattern":"codex\\.live-media-relay\\.ice-servers\\[(.+?)\\] uses unsupported scheme %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/codex_live.go","lineNumber":101,"sourceCode":"\t\tavailablePorts := int(c.UDPPortMax) - int(c.UDPPortMin) + 1\n\t\trequiredPorts := c.EffectiveMaxSessions() * 2\n\t\tif availablePorts < requiredPorts {\n\t\t\treturn fmt.Errorf(\"codex.live-media-relay UDP range requires at least %d ports for %d sessions\", requiredPorts, c.EffectiveMaxSessions())\n\t\t}\n\t}\n\tfor serverIndex, server := range c.ICEServers {\n\t\tif len(server.URLs) == 0 {\n\t\t\treturn fmt.Errorf(\"codex.live-media-relay.ice-servers[%d].urls is required\", serverIndex)\n\t\t}\n\t\tfor _, rawURL := range server.URLs {\n\t\t\tparsed, errParse := url.Parse(strings.TrimSpace(rawURL))\n\t\t\tif errParse != nil || parsed.Scheme == \"\" {\n\t\t\t\treturn fmt.Errorf(\"codex.live-media-relay.ice-servers[%d] contains an invalid URL\", serverIndex)\n\t\t\t}\n\t\t\tswitch strings.ToLower(parsed.Scheme) {\n\t\t\tcase \"stun\", \"stuns\", \"turn\", \"turns\":\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"codex.live-media-relay.ice-servers[%d] uses unsupported scheme %q\", serverIndex, parsed.Scheme)\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n","sourceCodeStart":83,"sourceCodeEnd":107,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/config/codex_live.go#L83-L107","documentation":"Only the STUN/TURN scheme family is supported for ICE servers: stun, stuns, turn, turns (case-insensitive). Any other scheme — http, https, or a misspelling — fails validation with the offending scheme echoed in the message.","triggerScenarios":"Validate's switch on strings.ToLower(parsed.Scheme) hits default — e.g. 'https://stun.example.com' used because it looked like a normal web endpoint, or 'stun:' fat-fingered as 'stunN:'.","commonSituations":"Users wrap the STUN host in https:// out of habit; tooling templates prepend https://; typo'd scheme after editing.","solutions":["Change the scheme to stun/stuns/turn/turns as appropriate: 'stun:host:19302' or 'turns:host:5349'.","For TURN over TLS use turns:, for STUN over TLS use stuns: — not https:.","Re-run config load after the fix; the message shows exactly which scheme was rejected."],"exampleFix":"# before\nurls:\n  - https://stun.example.com:19302\n\n# after\nurls:\n  - stuns:stun.example.com:19302","handlingStrategy":"validation","validationCode":"allowed := map[string]bool{\"stun\": true, \"stuns\": true, \"turn\": true, \"turns\": true}\nfor _, u := range server.URLs {\n\tp, _ := url.Parse(strings.TrimSpace(u))\n\tif !allowed[strings.ToLower(p.Scheme)] {\n\t\treturn fmt.Errorf(\"unsupported scheme %q\", p.Scheme)\n\t}\n}","typeGuard":"func isSupportedICEScheme(raw string) bool {\n\tp, err := url.Parse(strings.TrimSpace(raw))\n\tif err != nil { return false }\n\tswitch strings.ToLower(p.Scheme) {\n\tcase \"stun\", \"stuns\", \"turn\", \"turns\":\n\t\treturn true\n\t}\n\treturn false\n}","tryCatchPattern":null,"preventionTips":["Memorize the allowed set: stun/stuns/turn/turns; https:// is never valid here.","The error message echoes the rejected scheme — use it to locate the bad entry."],"tags":["config","validation","ice-servers","scheme","codex-live"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}