{"record":{"id":"7fd697cf4bccae38","repo":"router-for-me/CLIProxyAPI","slug":"close-tcp-candidate-tunnels-w","errorCode":null,"errorMessage":"close TCP candidate tunnels: %w","messagePattern":"close TCP candidate tunnels: %w","errorType":"http","errorClass":null,"httpStatus":502,"severity":"warning","filePath":"internal/client/codex/live/media.go","lineNumber":422,"sourceCode":"}\n\nfunc (s *pionMediaSession) AcceptUpstreamAnswer(ctx context.Context, upstreamAnswer string) (string, error) {\n\tif s == nil || s.upstream == nil || s.downstream == nil {\n\t\treturn \"\", errors.New(\"Codex live media session unavailable\")\n\t}\n\tanswerToApply := upstreamAnswer\n\tif s.proxyDialer != nil {\n\t\trewrittenAnswer, tunnels, errProxy := prepareProxiedUpstreamAnswer(upstreamAnswer, s.localOffer, s.proxyDialer)\n\t\tif errProxy != nil {\n\t\t\treturn \"\", errProxy\n\t\t}\n\t\tfor _, tunnel := range tunnels {\n\t\t\ttunnel.setForwardingStartedHandler(s.logForwardingStarted)\n\t\t}\n\t\tif !s.installCandidateTunnels(tunnels) {\n\t\t\terrClosed := errors.New(\"Codex live media session closed while configuring TCP proxy\")\n\t\t\tif errClose := closeCandidateTunnels(tunnels); errClose != nil {\n\t\t\t\treturn \"\", errors.Join(errClosed, fmt.Errorf(\"close TCP candidate tunnels: %w\", errClose))\n\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 {","sourceCodeStart":404,"sourceCodeEnd":440,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/client/codex/live/media.go#L404-L440","documentation":"A secondary, joined error (errors.Join) thrown when closing the TCP candidate tunnels fails after the session was already detected closed while configuring the TCP proxy. The primary error is 'Codex live media session closed while configuring TCP proxy'; this wrap only reports that the cleanup of the local TCP listeners (tunnels toward the proxy) also failed. Always accompanied by the primary closed-session error.","triggerScenarios":"Session.Close() runs concurrently with AcceptUpstreamAnswer while tunnels were prepared; installCandidateTunnels returns false because s.tunnels state shows the session closed, and then closeCandidateTunnels hits already-closed listeners.","commonSituations":"Client disconnect racing the upstream answer application in proxied mode; benign in effect (everything is being torn down anyway) but noisy in logs.","solutions":["Fix the primary race: prevent AcceptUpstreamAnswer from being called after/while Close (guard at the caller with session state)","Treat the joined close error as informational — the session is already terminating","Serialize Close and AcceptUpstreamAnswer with the session mutex if adding lifecycle guards"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if _, err := session.AcceptUpstreamAnswer(ctx, answer); err != nil {\n\tif strings.Contains(err.Error(), \"session closed while configuring TCP proxy\") {\n\t\t// benign teardown race; joined close error is diagnostic only\n\t\tlog.WithError(err).Debug(\"codex live: answer raced session close\")\n\t\treturn\n\t}\n\tlog.WithError(err).Error(\"codex live: apply upstream answer failed\")\n}","preventionTips":["Do not call AcceptUpstreamAnswer after triggering Close (guard with a session-state flag)","Log joined errors at debug — the session is already terminating","Fix the primary race, not the secondary close error"],"tags":["webrtc","tcp-tunnel","proxy","cleanup","concurrency"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}