router-for-me/CLIProxyAPI · error

add upstream audio track: %w

Error message

add upstream audio track: %w

What it means

Thrown when upstream.AddTrack(toOpenAI) fails attaching the OpenAI-facing audio sender. Unlike the downstream leg, there is no remote offer yet on upstream at this point, so failure typically comes from internal sender-state errors or repeated AddTrack with the same track ID in a degraded session rather than codec negotiation.

Source

Thrown at internal/client/codex/live/media.go:349

		_ = 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)
	}
	upstreamSender, errTrack := upstream.AddTrack(toOpenAI)
	if errTrack != nil {
		_ = session.Close()
		return nil, "", fmt.Errorf("add upstream audio track: %w", errTrack)
	}
	go drainRTCP("upstream", upstreamSender, session.done)

	downstream.OnTrack(func(track *webrtc.TrackRemote, _ *webrtc.RTPReceiver) {
		if !strings.EqualFold(track.Codec().MimeType, webrtc.MimeTypeOpus) {
			return
		}
		go relayRTP("downstream-to-upstream", track, toOpenAI, session.done)
	})
	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 {

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Retry the whole session establishment (NewSession) — setup is atomic and cleans up on failure
  2. Check pion logs for the underlying RTPSender error to distinguish resource vs state causes
  3. Keep pion/webrtc at a patched version (older v4 releases had sender race fixes)
Defensive patterns

Strategy: retry

Try / catch

if _, _, err := relay.NewSession(ctx, offer, route); err != nil {
	if strings.Contains(err.Error(), "add upstream audio track") && ctx.Err() == nil {
		_, _, err = relay.NewSession(ctx, offer, route) // setup is atomic; safe to retry
	}
}

Prevention

When it happens

Trigger: Internal RTP sender creation failure on the upstream PeerConnection, or state corruption from an earlier partial setup on the same PC.

Common situations: Rare in practice; usually preceded by other setup anomalies. Retryable at the session level since NewSession is atomic per attempt.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/c7add8513bc1befe. Report an issue: GitHub.