XTLS/Xray-core · error

unexpected client IP length

Error message

unexpected client IP length 

What it means

A padding schedule is an alternating dialogue: each turn must switch direction (client-to-server, then server-to-client, and so on). validatePaddingSchedule rejects two consecutive turns with the same direction, because both sides would simultaneously wait to read (deadlock) or both write (protocol desync).

Source

Thrown at app/dns/dns.go:49

	domainMatcher          geodata.DomainMatcher
	matcherInfos           []*DomainMatcherInfo
	checkSystem            bool
}

// DomainMatcherInfo contains information attached to index returned by Server.domainMatcher.
type DomainMatcherInfo struct {
	clientIdx  uint16
	domainRule string
}

// New creates a new DNS server with given configuration.
func New(ctx context.Context, config *Config) (*DNS, error) {
	var clientIP net.IP
	switch len(config.ClientIp) {
	case 0, net.IPv4len, net.IPv6len:
		clientIP = net.IP(config.ClientIp)
	default:
		return nil, errors.New("unexpected client IP length ", len(config.ClientIp))
	}

	var ipOption dns.IPOption
	checkSystem := false
	switch config.QueryStrategy {
	case QueryStrategy_USE_IP:
		ipOption = dns.IPOption{
			IPv4Enable: true,
			IPv6Enable: true,
			FakeEnable: false,
		}
	case QueryStrategy_USE_SYS:
		ipOption = dns.IPOption{
			IPv4Enable: true,
			IPv6Enable: true,
			FakeEnable: false,
		}
		checkSystem = true

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Flip the direction of turn i (or i-1) so consecutive turns alternate
  2. If both directions are genuinely needed back-to-back, merge the intent into one turn or insert a turn of the opposite direction between them
  3. Add a unit assertion over generated schedules that directions strictly alternate

Example fix

// before
[]paddingTurn{
  {direction: paddingClientToServer, minLength: 100, maxLength: 200},
  {direction: paddingClientToServer, minLength: 100, maxLength: 200},
}
// after
[]paddingTurn{
  {direction: paddingClientToServer, minLength: 100, maxLength: 200},
  {direction: paddingServerToClient, minLength: 100, maxLength: 200},
}
Defensive patterns

Strategy: validation

Validate before calling

func directionsAlternate(s []paddingTurn) bool {
    for i := 1; i < len(s); i++ {
        if s[i].direction == s[i-1].direction {
            return false
        }
    }
    return true
}

Prevention

When it happens

Trigger: schedule[i].direction == schedule[i-1].direction for any i > 0. Caught by validatePaddingSchedule at startup, before traffic.

Common situations: Inserting an extra client-to-server turn to add cover traffic without flipping the next turn's direction; generating schedules programmatically with a direction bug; hand-editing a copied schedule and dropping the alternation.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/c65113a4973dc1ee. Report an issue: GitHub.