router-for-me/CLIProxyAPI · error
set downstream WebRTC offer: %w
Error message
set downstream WebRTC offer: %w
What it means
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().
Source
Thrown at internal/client/codex/live/media.go:326
mediaSessionID: uuid.NewString(),
releaseSlot: releaseSlot,
proxyDialer: proxyDialer,
proxyScheme: proxyScheme(route.proxyURL),
credential: strings.TrimSpace(route.credential),
authIndex: strings.TrimSpace(route.authIndex),
}
session.bridge = newDataChannelBridge(session.done, func(err error) {
session.fail("data_channel_failed", err)
})
session.installStateHandlers()
log.WithFields(session.logFields("session")).Info("codex live WebRTC media session created")
if errRemote := downstream.SetRemoteDescription(webrtc.SessionDescription{
Type: webrtc.SDPTypeOffer,
SDP: clientOffer,
}); errRemote != nil {
_ = session.Close()
return nil, "", fmt.Errorf("set downstream WebRTC offer: %w", errRemote)
}
toDesktop, errTrack := webrtc.NewTrackLocalStaticRTP(opusCodec, "audio", "codex-live")
if errTrack != nil {
_ = session.Close()
return nil, "", fmt.Errorf("create downstream audio track: %w", errTrack)
}
downstreamSender, errTrack := downstream.AddTrack(toDesktop)
if errTrack != nil {
_ = session.Close()
return nil, "", fmt.Errorf("add downstream audio track: %w", errTrack)
}
go drainRTCP("downstream", downstreamSender, session.done)
toOpenAI, errTrack := webrtc.NewTrackLocalStaticRTP(opusCodec, "audio", "codex-live")
if errTrack != nil {
_ = session.Close()
return nil, "", fmt.Errorf("create upstream audio track: %w", errTrack)View on GitHub (pinned to 78f0c4079e)
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
Defensive patterns
Strategy: validation
Validate before calling
func isValidAudioOffer(sdp string) bool {
parsed := sdp.Unmarshal // use sdp/3rd-party parser; simplified check:
return strings.Contains(sdp, "m=audio") && strings.Contains(sdp, "opus")
} Try / catch
if _, _, err := relay.NewSession(ctx, clientOffer, route); err != nil {
if strings.Contains(err.Error(), "set downstream WebRTC offer") {
// reject the client with 400 rather than 500: the offer is unusable
respondBadRequest(w)
return
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- set upstream WebRTC answer: %w
- upstream WebRTC offer is empty
- downstream WebRTC answer is empty
- upstream WebRTC answer has no supported public TCP passive c
- upstream WebRTC TCP proxy candidate address must be an IP
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/28e88df01abfaba2.
Report an issue: GitHub.