{"record":{"id":"b603531a20d3a52f","repo":"router-for-me/CLIProxyAPI","slug":"upstream-webrtc-answer-has-no-supported-public-tcp","errorCode":null,"errorMessage":"upstream WebRTC answer has no supported public TCP passive candidate on port 443","messagePattern":"upstream WebRTC answer has no supported public TCP passive candidate on port 443","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"internal/client/codex/live/tcp_proxy.go","lineNumber":147,"sourceCode":"\t\t\tplan, keep, errCandidate := proxiedTCPCandidatePlan(attribute.Value)\n\t\t\tif errCandidate != nil {\n\t\t\t\treturn \"\", nil, errCandidate\n\t\t\t}\n\t\t\tif !keep {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif len(plans) >= maxProxiedTCPCandidates {\n\t\t\t\treturn \"\", nil, fmt.Errorf(\"upstream WebRTC answer exceeds the %d TCP candidate proxy limit\", maxProxiedTCPCandidates)\n\t\t\t}\n\t\t\tplan.mediaIndex = mediaIndex\n\t\t\tplan.attributeIndex = len(filtered)\n\t\t\tfiltered = append(filtered, attribute)\n\t\t\tplans = append(plans, plan)\n\t\t}\n\t\tmedia.Attributes = filtered\n\t}\n\tif len(plans) == 0 {\n\t\treturn \"\", nil, errors.New(\"upstream WebRTC answer has no supported public TCP passive candidate on port 443\")\n\t}\n\n\texpectedUser := remoteCredentials.ufrag + \":\" + localCredentials.ufrag\n\ttunnels := make([]*tcpCandidateTunnel, 0, len(plans))\n\tcloseTunnels := func() {\n\t\tfor _, tunnel := range tunnels {\n\t\t\tif errClose := tunnel.Close(); errClose != nil {\n\t\t\t\tlog.WithError(errClose).Debug(\"codex live TCP proxy: close candidate tunnel after setup error\")\n\t\t\t}\n\t\t}\n\t}\n\tfor _, plan := range plans {\n\t\ttunnel, errTunnel := newTCPCandidateTunnel(plan.target, dialer, expectedUser, remoteCredentials.password)\n\t\tif errTunnel != nil {\n\t\t\tcloseTunnels()\n\t\t\treturn \"\", nil, errTunnel\n\t\t}\n\t\ttunnels = append(tunnels, tunnel)","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/client/codex/live/tcp_proxy.go#L129-L165","documentation":"Thrown by prepareProxiedUpstreamAnswer when a TCP proxy dialer is configured and the upstream Codex WebRTC answer was parsed and filtered, but zero candidate lines survived. The proxy path only rewrites 'host/tcp/passive/RTP-component' candidates listening on port 443; if the upstream returned only UDP candidates, srflx/relay candidates, active TCP, or TCP on another port, plans ends up empty and the error is returned.","triggerScenarios":"Configuring the live media relay to dial the upstream through an HTTP CONNECT/SOCKS TCP proxy (proxyDialer != nil) while the upstream server's SDP contains no a=candidate ... tcp ... passive ... 443 host line.","commonSituations":"Upstream Codex endpoint changes its ICE strategy (e.g. prefers UDP-only or moves off port 443); a proxy environment where the server behind it advertises non-standard candidates; region-specific infrastructure differences.","solutions":["Capture the raw upstream answer SDP (log it at debug level) and inspect the a=candidate lines for tcp passive port 443 entries","If the upstream no longer offers TCP passive on 443, disable the TCP proxy dialer for this session so the relay uses direct connectivity","Verify the proxy dialer configuration actually points at a proxy the upstream supports — the server may serve different candidate sets depending on how the signaling connection was established","Report upstream candidate-set changes to the maintainers if the SDP genuinely lacks supported candidates"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"sdp, tunnels, err := live.PrepareProxiedUpstreamAnswer(answer, offer, dialer)\nif err != nil && strings.Contains(err.Error(), \"no supported public TCP passive candidate on port 443\") {\n    log.Warn(\"upstream lacks proxiable TCP candidates; falling back to direct connection\")\n    sdp, tunnels, err = answer, nil, nil // apply the raw answer without proxying\n}\nif err != nil {\n    return err\n}","preventionTips":["Before enabling the TCP proxy dialer, confirm the upstream advertises tcp passive port 443 host candidates","Log candidate lines at debug level in new deployments to detect upstream ICE strategy changes","Implement a fallback path that applies the unproxied answer when no proxyable candidate exists"],"tags":["webrtc","codex","tcp-proxy","ice-candidates","sdp"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}