router-for-me/CLIProxyAPI · error
create upstream DataChannel: %w
Error message
create upstream DataChannel: %w
What it means
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.
Source
Thrown at internal/client/codex/live/media.go:377
upstream.OnTrack(func(track *webrtc.TrackRemote, _ *webrtc.RTPReceiver) {
if !strings.EqualFold(track.Codec().MimeType, webrtc.MimeTypeOpus) {
return
}
go relayRTP("upstream-to-downstream", track, toDesktop, session.done)
})
downstream.OnDataChannel(func(channel *webrtc.DataChannel) {
if channel.Label() != realtimeDataChannelLabel {
if errClose := channel.Close(); errClose != nil {
log.WithError(errClose).Debug("codex live media: close unsupported downstream DataChannel")
}
return
}
session.bridge.attachDownstream(channel)
})
upstreamChannel, errChannel := upstream.CreateDataChannel(realtimeDataChannelLabel, nil)
if errChannel != nil {
_ = session.Close()
return nil, "", fmt.Errorf("create upstream DataChannel: %w", errChannel)
}
session.bridge.attachUpstream(upstreamChannel)
gatherComplete := webrtc.GatheringCompletePromise(upstream)
offer, errOffer := upstream.CreateOffer(nil)
if errOffer != nil {
_ = session.Close()
return nil, "", fmt.Errorf("create upstream WebRTC offer: %w", errOffer)
}
if errLocal := upstream.SetLocalDescription(offer); errLocal != nil {
_ = session.Close()
return nil, "", fmt.Errorf("set upstream WebRTC offer: %w", errLocal)
}
select {
case <-gatherComplete:
case <-ctx.Done():
_ = session.Close()
return nil, "", fmt.Errorf("gather upstream WebRTC candidates: %w", ctx.Err())View on GitHub (pinned to 78f0c4079e)
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
Defensive patterns
Strategy: retry
Try / catch
if _, _, err := relay.NewSession(ctx, offer, route); err != nil {
var iceErr *webrtc.InvalidStateError
if errors.As(err, &iceErr) {
// PC state race — retry whole session once
_, _, err = relay.NewSession(ctx, offer, route)
}
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: Race between session teardown (Close called from another goroutine) and setup; essentially a timing/concurrency edge rather than a config issue.
Related errors
- create upstream WebRTC offer: %w
- set upstream WebRTC offer: %w
- close TCP candidate tunnels: %w
- create downstream WebRTC answer: %w
- set downstream WebRTC answer: %w
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/427ab03028dcd66a.
Report an issue: GitHub.