XTLS/Xray-core · error

Failed to convert address to Net IP.

Error message

Failed to convert address to Net IP.

What it means

For turn 0 with variants, validatePaddingSchedule runs trimPaddingPrefix on every variant to prove the first-turn prefix can be consumed exactly. This error wraps a failure: either the prefix splits a chunk ('prefix length %d splits chunk %d') or it consumes all chunks ('prefix length %d leaves no padding record'). The variant's chunk boundaries must align so that whole chunks cover the prefix and at least one chunk remains for the padding record.

Source

Thrown at app/dns/config.go:31

var localTLDsAndDotlessDomainsRules = []*geodata.DomainRule{
	{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Regex, Value: "^[^.]+$"}}}, // This will only match domains without any dot
	{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "local"}}},
	{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "localdomain"}}},
	{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "localhost"}}},
	{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "lan"}}},
	{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "home.arpa"}}},
	{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "example"}}},
	{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "invalid"}}},
	{Value: &geodata.DomainRule_Custom{Custom: &geodata.Domain{Type: geodata.Domain_Domain, Value: "test"}}},
}

func toNetIP(addrs []net.Address) ([]net.IP, error) {
	ips := make([]net.IP, 0, len(addrs))
	for _, addr := range addrs {
		if addr.Family().IsIP() {
			ips = append(ips, addr.IP())
		} else {
			return nil, errors.New("Failed to convert address", addr, "to Net IP.").AtWarning()
		}
	}
	return ips, nil
}

func generateRandomTag() string {
	id := uuid.New()
	return "xray.system." + id.String()
}

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Re-shape variant chunks so a prefix-boundary subset of chunk lengths sums exactly to firstTurnPrefixLength and at least one chunk follows
  2. Or set firstTurnPrefixLength to a value that already equals a partial sum of every variant's chunk lengths
  3. As a last resort use a range-based (non-variant) first turn, which handles arbitrary prefixes via minLength > prefix

Example fix

// before: prefix 100 splits the 150-byte first chunk
paddingVariant{chunks: []int{150, 300}}
// after: 100 + 50 consumed as prefix-aligned chunks
paddingVariant{chunks: []int{100, 50, 300}}
Defensive patterns

Strategy: validation

Validate before calling

func variantFitsPrefix(v paddingVariant, prefix int) bool {
    remaining, i := prefix, 0
    for i < len(v.chunks) && remaining > 0 {
        if remaining < v.chunks[i] {
            return false
        }
        remaining -= v.chunks[i]
        i++
    }
    return remaining == 0 && i < len(v.chunks)
}

Prevention

When it happens

Trigger: Turn 0 has variants, firstTurnPrefixLength > 0, and some variant's cumulative chunk lengths never equal the prefix exactly (prefix falls mid-chunk, or prefix >= total variant length). Detected during schedule validation before I/O.

Common situations: Adding early-data prefix support to an existing variant schedule whose chunk sizes were chosen for traffic shaping, not prefix alignment; changing prefix length without re-cutting the variant chunks; variants of different total lengths where only some align.

Related errors


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