{"record":{"id":"9623bc5557f77b9e","repo":"router-for-me/CLIProxyAPI","slug":"set-upstream-webrtc-offer-w","errorCode":null,"errorMessage":"set upstream WebRTC offer: %w","messagePattern":"set upstream WebRTC offer: %w","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"internal/client/codex/live/media.go","lineNumber":389,"sourceCode":"\t\t}\n\t\tsession.bridge.attachDownstream(channel)\n\t})\n\tupstreamChannel, errChannel := upstream.CreateDataChannel(realtimeDataChannelLabel, nil)\n\tif errChannel != nil {\n\t\t_ = session.Close()\n\t\treturn nil, \"\", fmt.Errorf(\"create upstream DataChannel: %w\", errChannel)\n\t}\n\tsession.bridge.attachUpstream(upstreamChannel)\n\n\tgatherComplete := webrtc.GatheringCompletePromise(upstream)\n\toffer, errOffer := upstream.CreateOffer(nil)\n\tif errOffer != nil {\n\t\t_ = session.Close()\n\t\treturn nil, \"\", fmt.Errorf(\"create upstream WebRTC offer: %w\", errOffer)\n\t}\n\tif errLocal := upstream.SetLocalDescription(offer); errLocal != nil {\n\t\t_ = session.Close()\n\t\treturn nil, \"\", fmt.Errorf(\"set upstream WebRTC offer: %w\", errLocal)\n\t}\n\tselect {\n\tcase <-gatherComplete:\n\tcase <-ctx.Done():\n\t\t_ = session.Close()\n\t\treturn nil, \"\", fmt.Errorf(\"gather upstream WebRTC candidates: %w\", ctx.Err())\n\t}\n\tlocalDescription := upstream.LocalDescription()\n\tif localDescription == nil || strings.TrimSpace(localDescription.SDP) == \"\" {\n\t\t_ = session.Close()\n\t\treturn nil, \"\", errors.New(\"upstream WebRTC offer is empty\")\n\t}\n\tsession.localOffer = localDescription.SDP\n\treturn session, localDescription.SDP, nil\n}\n\nfunc (s *pionMediaSession) AcceptUpstreamAnswer(ctx context.Context, upstreamAnswer string) (string, error) {\n\tif s == nil || s.upstream == nil || s.downstream == nil {","sourceCodeStart":371,"sourceCodeEnd":407,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/client/codex/live/media.go#L371-L407","documentation":"Thrown when upstream.SetLocalDescription(offer) fails while committing the locally generated offer. After SetLocalDescription, ICE candidate gathering begins (the gatherComplete promise below). Pion fails this when the description state is inconsistent (offer already set, PC closing) or the SDP cannot be re-parsed — typically a state race with session teardown.","triggerScenarios":"Concurrent Close() during setup, double SetLocalDescription, or ICE agent shutdown initiated between CreateOffer and SetLocalDescription.","commonSituations":"Handshake-timeout handlers firing too aggressively and tearing down the session mid-offer; client cancellation racing setup.","solutions":["Retry session establishment if the client is still connected","Ensure handshake timeouts do not close the session before SetLocalDescription completes","Keep pion/webrtc current for ICE-agent state race fixes"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if errLocal := upstream.SetLocalDescription(offer); errLocal != nil {\n\tif upstream.ConnectionState() == webrtc.PeerConnectionStateClosed {\n\t\treturn nil, \"\", fmt.Errorf(\"upstream closed during offer commit: %w\", errLocal)\n\t}\n\treturn nil, \"\", errLocal\n}","preventionTips":["Set local description immediately after CreateOffer with no await points in between","Ensure handshake timeouts exceed worst-case setup time","Treat repeated occurrences as a teardown-race bug in caller lifecycle management"],"tags":["webrtc","sdp","ice","concurrency","state-machine"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}