{"record":{"id":"9b4e68b7f705e87c","repo":"router-for-me/CLIProxyAPI","slug":"create-downstream-webrtc-answer-w","errorCode":null,"errorMessage":"create downstream WebRTC answer: %w","messagePattern":"create downstream WebRTC answer: %w","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"internal/client/codex/live/media.go","lineNumber":441,"sourceCode":"\t\t\t}\n\t\t\treturn \"\", errClosed\n\t\t}\n\t\tanswerToApply = rewrittenAnswer\n\t}\n\tif errRemote := s.upstream.SetRemoteDescription(webrtc.SessionDescription{\n\t\tType: webrtc.SDPTypeAnswer,\n\t\tSDP:  answerToApply,\n\t}); errRemote != nil {\n\t\terrSetRemote := fmt.Errorf(\"set upstream WebRTC answer: %w\", errRemote)\n\t\tif errClose := s.closeCandidateTunnels(); errClose != nil {\n\t\t\treturn \"\", errors.Join(errSetRemote, fmt.Errorf(\"close TCP candidate tunnels: %w\", errClose))\n\t\t}\n\t\treturn \"\", errSetRemote\n\t}\n\tgatherComplete := webrtc.GatheringCompletePromise(s.downstream)\n\tanswer, errAnswer := s.downstream.CreateAnswer(nil)\n\tif errAnswer != nil {\n\t\treturn \"\", fmt.Errorf(\"create downstream WebRTC answer: %w\", errAnswer)\n\t}\n\tif errLocal := s.downstream.SetLocalDescription(answer); errLocal != nil {\n\t\treturn \"\", fmt.Errorf(\"set downstream WebRTC answer: %w\", errLocal)\n\t}\n\tselect {\n\tcase <-gatherComplete:\n\tcase <-ctx.Done():\n\t\treturn \"\", fmt.Errorf(\"gather downstream WebRTC candidates: %w\", ctx.Err())\n\t}\n\tlocalDescription := s.downstream.LocalDescription()\n\tif localDescription == nil || strings.TrimSpace(localDescription.SDP) == \"\" {\n\t\treturn \"\", errors.New(\"downstream WebRTC answer is empty\")\n\t}\n\treturn localDescription.SDP, nil\n}\n\nfunc (s *pionMediaSession) installCandidateTunnels(tunnels []*tcpCandidateTunnel) bool {\n\tif s == nil {","sourceCodeStart":423,"sourceCodeEnd":459,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/client/codex/live/media.go#L423-L459","documentation":"Thrown when downstream.CreateAnswer(nil) fails generating the SDP answer returned to the local client. By this point the client offer was applied (SetRemoteDescription succeeded) and the Opus track was added, so failure means the downstream PC cannot produce an answer — state is wrong (closed/closing), or the negotiated media cannot be marshaled. Often the downstream leg was torn down while the upstream leg was being set up.","triggerScenarios":"Downstream PC closed concurrently (client disconnected during the upstream answer round-trip), or an SDP generation failure after the offer/track setup.","commonSituations":"Slow upstream negotiation exceeding the client's patience; the client closes its PC and the relay's downstream PC transitions to closed before CreateAnswer.","solutions":["Reduce upstream answer latency (proxy hop adds the most)","Retry the full session when the client reconnects","Guard against calling AcceptUpstreamAnswer after the downstream PC state is 'closed' (check ICEConnectionState)"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"func downstreamStillLive(s *pionMediaSession) bool {\n\treturn s.downstream != nil &&\n\t\ts.downstream.ConnectionState() != webrtc.PeerConnectionStateClosed &&\n\t\ts.downstream.ConnectionState() != webrtc.PeerConnectionStateFailed\n}","typeGuard":null,"tryCatchPattern":"if _, err := session.AcceptUpstreamAnswer(ctx, answer); err != nil {\n\tif strings.Contains(err.Error(), \"create downstream WebRTC answer\") {\n\t\t// downstream leg died during upstream round-trip; full session retry is the only recovery\n\t\tsession.CloseWithReason(\"downstream_lost\")\n\t}\n}","preventionTips":["Minimize upstream answer latency so the downstream PC stays live","Check downstream connection state before applying the upstream answer","Recover only via a brand-new session, never by reusing this one"],"tags":["webrtc","sdp","answer","downstream","concurrency"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}