{"record":{"id":"7bf117b61113201c","repo":"router-for-me/CLIProxyAPI","slug":"set-downstream-webrtc-answer-w","errorCode":null,"errorMessage":"set downstream WebRTC answer: %w","messagePattern":"set downstream WebRTC answer: %w","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"internal/client/codex/live/media.go","lineNumber":444,"sourceCode":"\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 {\n\t\treturn false\n\t}\n\ts.tunnelsMu.Lock()","sourceCodeStart":426,"sourceCodeEnd":462,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/client/codex/live/media.go#L426-L462","documentation":"Thrown when downstream.SetLocalDescription(answer) fails committing the client-facing answer, which also kicks off downstream ICE gathering. Pion fails when the description state is inconsistent (answer already applied, PC closing) — in practice a teardown race or double-call of AcceptUpstreamAnswer.","triggerScenarios":"AcceptUpstreamAnswer invoked twice for one session, or session Close racing the local description commit on the downstream PC.","commonSituations":"Duplicate answer-delivery retries from the caller, or client disconnect during the final answer step.","solutions":["Ensure AcceptUpstreamAnswer is called at most once per session (wrap with sync.Once or a state flag at the caller)","Retry via a fresh session if the client reconnects","Check PC ConnectionState before applying if adding a precondition"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func (s *pionMediaSession) canApplyDownstreamAnswer() bool {\n\treturn s != nil && s.downstream != nil &&\n\t\ts.downstream.SignalingState() == webrtc.SignalingStateHaveRemoteOffer\n}","tryCatchPattern":"if errLocal := s.downstream.SetLocalDescription(answer); errLocal != nil {\n\tvar stateErr *webrtc.InvalidStateError\n\tif errors.As(errLocal, &stateErr) {\n\t\t// double-call or teardown race; answer already committed elsewhere\n\t\treturn \"\", fmt.Errorf(\"downstream answer state conflict: %w\", errLocal)\n\t}\n\treturn \"\", errLocal\n}","preventionTips":["Call AcceptUpstreamAnswer exactly once per session (guard with sync.Once at the caller)","Check SignalingState before committing if adding preconditions","Route duplicate answer deliveries to a no-op instead of an error path"],"tags":["webrtc","sdp","state-machine","concurrency","downstream"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}