{"record":{"id":"28e88df01abfaba2","repo":"router-for-me/CLIProxyAPI","slug":"set-downstream-webrtc-offer-w","errorCode":null,"errorMessage":"set downstream WebRTC offer: %w","messagePattern":"set downstream WebRTC offer: %w","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"internal/client/codex/live/media.go","lineNumber":326,"sourceCode":"\t\tmediaSessionID: uuid.NewString(),\n\t\treleaseSlot:    releaseSlot,\n\t\tproxyDialer:    proxyDialer,\n\t\tproxyScheme:    proxyScheme(route.proxyURL),\n\t\tcredential:     strings.TrimSpace(route.credential),\n\t\tauthIndex:      strings.TrimSpace(route.authIndex),\n\t}\n\tsession.bridge = newDataChannelBridge(session.done, func(err error) {\n\t\tsession.fail(\"data_channel_failed\", err)\n\t})\n\tsession.installStateHandlers()\n\tlog.WithFields(session.logFields(\"session\")).Info(\"codex live WebRTC media session created\")\n\n\tif errRemote := downstream.SetRemoteDescription(webrtc.SessionDescription{\n\t\tType: webrtc.SDPTypeOffer,\n\t\tSDP:  clientOffer,\n\t}); errRemote != nil {\n\t\t_ = session.Close()\n\t\treturn nil, \"\", fmt.Errorf(\"set downstream WebRTC offer: %w\", errRemote)\n\t}\n\n\ttoDesktop, errTrack := webrtc.NewTrackLocalStaticRTP(opusCodec, \"audio\", \"codex-live\")\n\tif errTrack != nil {\n\t\t_ = session.Close()\n\t\treturn nil, \"\", fmt.Errorf(\"create downstream audio track: %w\", errTrack)\n\t}\n\tdownstreamSender, errTrack := downstream.AddTrack(toDesktop)\n\tif errTrack != nil {\n\t\t_ = session.Close()\n\t\treturn nil, \"\", fmt.Errorf(\"add downstream audio track: %w\", errTrack)\n\t}\n\tgo drainRTCP(\"downstream\", downstreamSender, session.done)\n\n\ttoOpenAI, errTrack := webrtc.NewTrackLocalStaticRTP(opusCodec, \"audio\", \"codex-live\")\n\tif errTrack != nil {\n\t\t_ = session.Close()\n\t\treturn nil, \"\", fmt.Errorf(\"create upstream audio track: %w\", errTrack)","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/client/codex/live/media.go#L308-L344","documentation":"Thrown when downstream.SetRemoteDescription fails while applying the client's WebRTC offer SDP to the downstream PeerConnection. Pion rejects offers that are not valid SDP, use an incompatible SDP semantics (unified-plan vs plan-b), or reference media the MediaEngine cannot negotiate (here: audio-only Opus). The half-built session is closed via session.Close().","triggerScenarios":"clientOffer is empty, truncated, or not an SDP; the offer carries video m-lines or codecs the engine (Opus/111 only) cannot answer; SDP session-level parse failure.","commonSituations":"A client SDK or non-audio client sending video/screen-share offers, a corrupted offer passed through an intermediate transport, or a client library that emits plan-b SDP.","solutions":["Confirm the client offer is a complete, base64/JSON-encoded SDP of type 'offer'","Restrict clients to audio-only Opus sessions — the relay registers no video codecs","Log the offer's m-lines at debug level to identify unsupported media","Update the client WebRTC stack to a unified-plan capable version"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"func isValidAudioOffer(sdp string) bool {\n\tparsed := sdp.Unmarshal // use sdp/3rd-party parser; simplified check:\n\treturn strings.Contains(sdp, \"m=audio\") && strings.Contains(sdp, \"opus\")\n}","typeGuard":null,"tryCatchPattern":"if _, _, err := relay.NewSession(ctx, clientOffer, route); err != nil {\n\tif strings.Contains(err.Error(), \"set downstream WebRTC offer\") {\n\t\t// reject the client with 400 rather than 500: the offer is unusable\n\t\trespondBadRequest(w)\n\t\treturn\n\t}\n}","preventionTips":["Pre-screen client offers for an Opus audio m-line before session setup","Map this error to HTTP 400 at the API layer — it is caller input, not server failure","Keep clients on unified-plan capable SDK versions"],"tags":["webrtc","sdp","negotiation","client-input"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}