{"record":{"id":"c7add8513bc1befe","repo":"router-for-me/CLIProxyAPI","slug":"add-upstream-audio-track-w","errorCode":null,"errorMessage":"add upstream audio track: %w","messagePattern":"add upstream audio track: %w","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"internal/client/codex/live/media.go","lineNumber":349,"sourceCode":"\t\t_ = session.Close()\n\t\treturn nil, \"\", fmt.Errorf(\"create downstream audio track: %w\", errTrack)\n\t}\n\tdownstreamSender, errTrack := downstream.AddTrack(toDesktop)\n\tif errTrack != nil {\n\t\t_ = session.Close()\n\t\treturn nil, \"\", fmt.Errorf(\"add downstream audio track: %w\", errTrack)\n\t}\n\tgo drainRTCP(\"downstream\", downstreamSender, session.done)\n\n\ttoOpenAI, errTrack := webrtc.NewTrackLocalStaticRTP(opusCodec, \"audio\", \"codex-live\")\n\tif errTrack != nil {\n\t\t_ = session.Close()\n\t\treturn nil, \"\", fmt.Errorf(\"create upstream audio track: %w\", errTrack)\n\t}\n\tupstreamSender, errTrack := upstream.AddTrack(toOpenAI)\n\tif errTrack != nil {\n\t\t_ = session.Close()\n\t\treturn nil, \"\", fmt.Errorf(\"add upstream audio track: %w\", errTrack)\n\t}\n\tgo drainRTCP(\"upstream\", upstreamSender, session.done)\n\n\tdownstream.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(\"downstream-to-upstream\", track, toOpenAI, session.done)\n\t})\n\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 {","sourceCodeStart":331,"sourceCodeEnd":367,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/client/codex/live/media.go#L331-L367","documentation":"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.","triggerScenarios":"Internal RTP sender creation failure on the upstream PeerConnection, or state corruption from an earlier partial setup on the same PC.","commonSituations":"Rare in practice; usually preceded by other setup anomalies. Retryable at the session level since NewSession is atomic per attempt.","solutions":["Retry the whole session establishment (NewSession) — setup is atomic and cleans up on failure","Check pion logs for the underlying RTPSender error to distinguish resource vs state causes","Keep pion/webrtc at a patched version (older v4 releases had sender race fixes)"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if _, _, err := relay.NewSession(ctx, offer, route); err != nil {\n\tif strings.Contains(err.Error(), \"add upstream audio track\") && ctx.Err() == nil {\n\t\t_, _, err = relay.NewSession(ctx, offer, route) // setup is atomic; safe to retry\n\t}\n}","preventionTips":["Depend on NewSession's atomic cleanup semantics for retries","Track failure rate; sustained failures indicate environment problems, not transient ones","Stay current on pion v4 patch releases"],"tags":["webrtc","audio","upstream","track"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}