{"record":{"id":"4dfecf1044548402","repo":"router-for-me/CLIProxyAPI","slug":"create-upstream-webrtc-offer-w","errorCode":null,"errorMessage":"create upstream WebRTC offer: %w","messagePattern":"create upstream WebRTC offer: %w","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"internal/client/codex/live/media.go","lineNumber":385,"sourceCode":"\t\t\tif errClose := channel.Close(); errClose != nil {\n\t\t\t\tlog.WithError(errClose).Debug(\"codex live media: close unsupported downstream DataChannel\")\n\t\t\t}\n\t\t\treturn\n\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","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/client/codex/live/media.go#L367-L403","documentation":"Thrown when upstream.CreateOffer(nil) fails while generating the SDP offer to send to OpenAI. Pion CreateOffer errors when the connection state disallows offering (already closed/closing), when there are no media or data channels to describe, or on SDP marshaling failure. Here the DataChannel and audio track were just added, so state races dominate.","triggerScenarios":"Upstream PC entered 'closed' state concurrently (session.Close raced setup), or internal SDP generation failure after adding the track/channel.","commonSituations":"Client disconnects mid-handshake causing teardown to race offer creation; rare pion internal errors.","solutions":["Retry the full session (new NewSession) when the client remains connected","Audit for goroutines that call session.Close() during setup (bridge failure handler fires 'data_channel_failed')","Align pion/webrtc version to pick up offer-generation fixes"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"offer, errOffer := upstream.CreateOffer(nil)\nif errOffer != nil {\n\tif pcState := upstream.ConnectionState(); pcState == webrtc.PeerConnectionStateClosed {\n\t\treturn nil, \"\", fmt.Errorf(\"session closed before offer: %w\", errOffer)\n\t}\n\t// transient: retry once after brief backoff\n\ttime.Sleep(50 * time.Millisecond)\n\toffer, errOffer = upstream.CreateOffer(nil)\n}","preventionTips":["Check upstream.ConnectionState() before offer creation in custom flows","Serialize setup steps with session lifecycle (done channel) to avoid closed-state offers","Retry at session granularity in callers"],"tags":["webrtc","sdp","offer","concurrency"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}