{"record":{"id":"8ddb9a6dfadfccf0","repo":"router-for-me/CLIProxyAPI","slug":"marshal-proxied-upstream-webrtc-answer-w","errorCode":null,"errorMessage":"marshal proxied upstream WebRTC answer: %w","messagePattern":"marshal proxied upstream WebRTC answer: %w","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"internal/client/codex/live/tcp_proxy.go","lineNumber":180,"sourceCode":"\t\t\tcloseTunnels()\n\t\t\treturn \"\", nil, errTunnel\n\t\t}\n\t\ttunnels = append(tunnels, tunnel)\n\t\tlistenerAddress, ok := tunnel.listener.Addr().(*net.TCPAddr)\n\t\tif !ok || listenerAddress.IP == nil {\n\t\t\tcloseTunnels()\n\t\t\treturn \"\", nil, errors.New(\"Codex live TCP proxy listener returned an invalid address\")\n\t\t}\n\t\tfields := append([]string(nil), plan.fields...)\n\t\tfields[4] = listenerAddress.IP.String()\n\t\tfields[5] = strconv.Itoa(listenerAddress.Port)\n\t\tremoteDescription.MediaDescriptions[plan.mediaIndex].Attributes[plan.attributeIndex].Value = strings.Join(fields, \" \")\n\t}\n\n\trewritten, errMarshal := remoteDescription.Marshal()\n\tif errMarshal != nil {\n\t\tcloseTunnels()\n\t\treturn \"\", nil, fmt.Errorf(\"marshal proxied upstream WebRTC answer: %w\", errMarshal)\n\t}\n\treturn string(rewritten), tunnels, nil\n}\n\nfunc proxiedTCPCandidatePlan(rawCandidate string) (tcpCandidatePlan, bool, error) {\n\ttrimmed := strings.TrimSpace(rawCandidate)\n\tcandidate, errCandidate := ice.UnmarshalCandidate(trimmed)\n\tif errCandidate != nil {\n\t\treturn tcpCandidatePlan{}, false, fmt.Errorf(\"parse upstream WebRTC candidate: %w\", errCandidate)\n\t}\n\tif candidate.NetworkType() != ice.NetworkTypeTCP4 && candidate.NetworkType() != ice.NetworkTypeTCP6 {\n\t\treturn tcpCandidatePlan{}, false, nil\n\t}\n\tif candidate.TCPType() != ice.TCPTypePassive {\n\t\treturn tcpCandidatePlan{}, false, nil\n\t}\n\tif candidate.Component() != uint16(ice.ComponentRTP) || candidate.Type() != ice.CandidateTypeHost {\n\t\treturn tcpCandidatePlan{}, false, nil","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/client/codex/live/tcp_proxy.go#L162-L198","documentation":"After rewriting candidate addresses to the loopback tunnel listeners, the proxy re-serializes the SDP. pion/sdp's Marshal failing at this point is nearly impossible for a parsed-then-mutated document; it indicates the rewrite corrupted invariants. On failure the already-created tunnels are closed (closeTunnels) before returning.","triggerScenarios":"remoteDescription.Marshal() fails after fields[4]/fields[5] of candidate lines were replaced — e.g. a rewritten value contains characters that break SDP attribute encoding, or the struct was left in an invalid state.","commonSituations":"Extremely rare in practice; most likely after local modifications to the rewrite logic (writing an IPv6 address with brackets into a field expecting a bare IP, or writing a non-numeric port).","solutions":["Log the rewritten candidate lines before Marshal and check the substituted IP/port values are bare IPs and integers.","If you modified the rewrite, ensure listenerAddress.IP.String() is not '[::1]' — use the IP without brackets for IPv6 loopback (netip's String() already omits them; a net.TCPAddr-based path may not).","Reproduce with tcp_proxy_test.go fixtures; Marshal failures on unmodified code should be reported upstream with the SDP."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"rewritten, tunnels, err := prepareProxiedUpstreamAnswer(answer, offer, dialer)\nif err != nil {\n\t// tunnels are already closed by the callee on marshal failure\n\tif strings.Contains(err.Error(), \"marshal proxied\") {\n\t\tlog.WithError(err).Error(\"SDP rewrite produced unmarshalable output\")\n\t}\n\treturn err\n}","preventionTips":["When modifying the rewrite, only substitute bare IPs and integer ports into candidate fields.","Unit-test the full parse->rewrite->marshal round trip on realistic SDP fixtures."],"tags":["webrtc","sdp","tcp-proxy","marshalling","codex-live"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}