{"record":{"id":"427ab03028dcd66a","repo":"router-for-me/CLIProxyAPI","slug":"create-upstream-datachannel-w","errorCode":null,"errorMessage":"create upstream DataChannel: %w","messagePattern":"create upstream DataChannel: %w","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"internal/client/codex/live/media.go","lineNumber":377,"sourceCode":"\tupstream.OnTrack(func(track *webrtc.TrackRemote, _ *webrtc.RTPReceiver) {\n\t\tif !strings.EqualFold(track.Codec().MimeType, webrtc.MimeTypeOpus) {\n\t\t\treturn\n\t\t}\n\t\tgo relayRTP(\"upstream-to-downstream\", track, toDesktop, session.done)\n\t})\n\tdownstream.OnDataChannel(func(channel *webrtc.DataChannel) {\n\t\tif channel.Label() != realtimeDataChannelLabel {\n\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())","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/client/codex/live/media.go#L359-L395","documentation":"Thrown when upstream.CreateDataChannel(\"oai-events\", nil) fails while creating the OpenAI realtime events DataChannel on the upstream PeerConnection. Pion errors here when the SCTP transport is in an invalid state, the label/protocol exceeds limits, or too many channels exist. The session is closed on failure.","triggerScenarios":"Calling CreateDataChannel on a PC whose SCTP association is already closing/closed (e.g. session torn down concurrently), or exceeding the 65534 channel limit (not realistic here).","commonSituations":"Race between session teardown (Close called from another goroutine) and setup; essentially a timing/concurrency edge rather than a config issue.","solutions":["Ensure AcceptUpstreamAnswer/NewSession are not racing with Close from disconnect handlers","Retry session establishment once if the client reconnects immediately","Upgrade pion/webrtc — SCTP state races have been patched across v4 minors"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if _, _, err := relay.NewSession(ctx, offer, route); err != nil {\n\tvar iceErr *webrtc.InvalidStateError\n\tif errors.As(err, &iceErr) {\n\t\t// PC state race — retry whole session once\n\t\t_, _, err = relay.NewSession(ctx, offer, route)\n\t}\n}","preventionTips":["Never call session.Close() from handlers that can fire during setup","Delay close-handler installation until setup completes (as installStateHandlers ordering does)","Retry at the session level, never by reusing a half-built session"],"tags":["webrtc","data-channel","sctp","concurrency"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}