router-for-me/CLIProxyAPI · error

SDP contains incomplete ICE credentials

Error message

SDP contains incomplete ICE credentials

What it means

Thrown while extracting ICE credentials from the upstream SDP for TCP proxying: a media section (or session + media combination) supplied only one of ice-ufrag / ice-pwd. Both are required to authenticate the rewritten candidates, so the proxy refuses to continue with half-present credentials.

Source

Thrown at internal/client/codex/live/tcp_proxy.go:259

	for _, media := range description.MediaDescriptions {
		if media == nil {
			continue
		}
		ufrag := sessionUfrag
		if mediaUfrag, ok := media.Attribute("ice-ufrag"); ok {
			ufrag = mediaUfrag
		}
		password := sessionPassword
		if mediaPassword, ok := media.Attribute("ice-pwd"); ok {
			password = mediaPassword
		}
		ufrag = strings.TrimSpace(ufrag)
		password = strings.TrimSpace(password)
		if ufrag == "" && password == "" {
			continue
		}
		if ufrag == "" || password == "" {
			return iceCredentials{}, errors.New("SDP contains incomplete ICE credentials")
		}
		current := iceCredentials{ufrag: ufrag, password: password}
		if selected.ufrag == "" {
			selected = current
			continue
		}
		if selected != current {
			return iceCredentials{}, errors.New("SDP contains inconsistent bundled ICE credentials")
		}
	}
	if selected.ufrag == "" {
		selected = iceCredentials{ufrag: strings.TrimSpace(sessionUfrag), password: strings.TrimSpace(sessionPassword)}
	}
	if selected.ufrag == "" || selected.password == "" {
		return iceCredentials{}, errors.New("SDP is missing ICE credentials")
	}
	return selected, nil
}

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Dump the answer SDP and check every a=ice-ufrag has a matching a=ice-pwd (session-level and per-m-line)
  2. Fix the SDP producer if it is under your control
  3. Otherwise disable TCP proxying for that session and report the malformed answer upstream
Defensive patterns

Strategy: validation

Validate before calling

// Check ICE credential completeness before proxying
ufrag := sdp.Session.Attribute("ice-ufrag")
pwd := sdp.Session.Attribute("ice-pwd")
for _, m := range sdp.MediaDescriptions {
    if v, ok := m.Attribute("ice-ufrag"); ok { ufrag = v }
    if v, ok := m.Attribute("ice-pwd"); ok { pwd = v }
}
if (ufrag == "") != (pwd == "") {
    return errors.New("answer SDP has half-present ICE credentials; proxy will reject")
}

Type guard

func hasCompleteIceCredentials(ufrag, pwd string) bool {
    return (strings.TrimSpace(ufrag) == "") == (strings.TrimSpace(pwd) == "")
}

Try / catch

if _, _, err := live.PrepareProxiedUpstreamAnswer(answer, offer, dialer); err != nil {
    if strings.Contains(err.Error(), "incomplete ICE credentials") {
        return fmt.Errorf("upstream SDP bug (missing ice-ufrag or ice-pwd); disable proxying or fix producer: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Upstream answer SDP where a m= section defines ice-ufrag without ice-pwd or vice versa (including the case where one comes from the session level and the other is absent everywhere).

Common situations: Buggy upstream SDP generation, or a signaling middlebox stripping one of the attributes; extremely rarely, hand-crafted SDP in tests.

Related errors


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