{"record":{"id":"0ed8dbf798569d4c","repo":"router-for-me/CLIProxyAPI","slug":"sdp-contains-incomplete-ice-credentials","errorCode":null,"errorMessage":"SDP contains incomplete ICE credentials","messagePattern":"SDP contains incomplete ICE credentials","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"internal/client/codex/live/tcp_proxy.go","lineNumber":259,"sourceCode":"\tfor _, media := range description.MediaDescriptions {\n\t\tif media == nil {\n\t\t\tcontinue\n\t\t}\n\t\tufrag := sessionUfrag\n\t\tif mediaUfrag, ok := media.Attribute(\"ice-ufrag\"); ok {\n\t\t\tufrag = mediaUfrag\n\t\t}\n\t\tpassword := sessionPassword\n\t\tif mediaPassword, ok := media.Attribute(\"ice-pwd\"); ok {\n\t\t\tpassword = mediaPassword\n\t\t}\n\t\tufrag = strings.TrimSpace(ufrag)\n\t\tpassword = strings.TrimSpace(password)\n\t\tif ufrag == \"\" && password == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif ufrag == \"\" || password == \"\" {\n\t\t\treturn iceCredentials{}, errors.New(\"SDP contains incomplete ICE credentials\")\n\t\t}\n\t\tcurrent := iceCredentials{ufrag: ufrag, password: password}\n\t\tif selected.ufrag == \"\" {\n\t\t\tselected = current\n\t\t\tcontinue\n\t\t}\n\t\tif selected != current {\n\t\t\treturn iceCredentials{}, errors.New(\"SDP contains inconsistent bundled ICE credentials\")\n\t\t}\n\t}\n\tif selected.ufrag == \"\" {\n\t\tselected = iceCredentials{ufrag: strings.TrimSpace(sessionUfrag), password: strings.TrimSpace(sessionPassword)}\n\t}\n\tif selected.ufrag == \"\" || selected.password == \"\" {\n\t\treturn iceCredentials{}, errors.New(\"SDP is missing ICE credentials\")\n\t}\n\treturn selected, nil\n}","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/client/codex/live/tcp_proxy.go#L241-L277","documentation":"Thrown while extracting ICE credentials from the upstream SDP for TCP proxying: a media section (or session + media combination) supplied only one of ice-ufrag / ice-pwd. Both are required to authenticate the rewritten candidates, so the proxy refuses to continue with half-present credentials.","triggerScenarios":"Upstream answer SDP where a m= section defines ice-ufrag without ice-pwd or vice versa (including the case where one comes from the session level and the other is absent everywhere).","commonSituations":"Buggy upstream SDP generation, or a signaling middlebox stripping one of the attributes; extremely rarely, hand-crafted SDP in tests.","solutions":["Dump the answer SDP and check every a=ice-ufrag has a matching a=ice-pwd (session-level and per-m-line)","Fix the SDP producer if it is under your control","Otherwise disable TCP proxying for that session and report the malformed answer upstream"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Check ICE credential completeness before proxying\nufrag := sdp.Session.Attribute(\"ice-ufrag\")\npwd := sdp.Session.Attribute(\"ice-pwd\")\nfor _, m := range sdp.MediaDescriptions {\n    if v, ok := m.Attribute(\"ice-ufrag\"); ok { ufrag = v }\n    if v, ok := m.Attribute(\"ice-pwd\"); ok { pwd = v }\n}\nif (ufrag == \"\") != (pwd == \"\") {\n    return errors.New(\"answer SDP has half-present ICE credentials; proxy will reject\")\n}","typeGuard":"func hasCompleteIceCredentials(ufrag, pwd string) bool {\n    return (strings.TrimSpace(ufrag) == \"\") == (strings.TrimSpace(pwd) == \"\")\n}","tryCatchPattern":"if _, _, err := live.PrepareProxiedUpstreamAnswer(answer, offer, dialer); err != nil {\n    if strings.Contains(err.Error(), \"incomplete ICE credentials\") {\n        return fmt.Errorf(\"upstream SDP bug (missing ice-ufrag or ice-pwd); disable proxying or fix producer: %w\", err)\n    }\n    return err\n}","preventionTips":["Pair every a=ice-ufrag with a=ice-pwd in generated SDP","Reject half-credentialed SDP early in tests for your own signaling code"],"tags":["webrtc","sdp","ice","credentials","tcp-proxy"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}