XTLS/Xray-core · error

failed to create hosts

Error message

failed to create hosts

What it means

After selecting a target length, writePaddingTurnWithBuffer computes recordLength = targetLength - prefixLength and requires it to be at least 1, because a padding record must contain its varint header plus payload. This error means the selected variant's total length (or a send-range draw) is not larger than the prefix that was already sent inside it, leaving no room for a record.

Source

Thrown at app/dns/dns.go:86

	case QueryStrategy_USE_IP4:
		ipOption = dns.IPOption{
			IPv4Enable: true,
			IPv6Enable: false,
			FakeEnable: false,
		}
	case QueryStrategy_USE_IP6:
		ipOption = dns.IPOption{
			IPv4Enable: false,
			IPv6Enable: true,
			FakeEnable: false,
		}
	default:
		return nil, errors.New("unexpected query strategy ", config.QueryStrategy)
	}

	hosts, err := NewStaticHosts(config.StaticHosts)
	if err != nil {
		return nil, errors.New("failed to create hosts").Base(err)
	}

	defaultTag := config.Tag
	if len(config.Tag) == 0 {
		defaultTag = generateRandomTag()
	}

	clients := make([]*Client, 0, len(config.NameServer))
	matcherInfos := make([]*DomainMatcherInfo, 0)
	effectiveRules := make([]*geodata.DomainRule, 0)

	for _, ns := range config.NameServer {
		clientIdx := len(clients)
		updateRules := func(isLocalNameServer bool) {
			// Prioritize local domains with specific TLDs or those without any dot for the local DNS
			if isLocalNameServer {
				effectiveRules = append(effectiveRules, localTLDsAndDotlessDomainsRules...)
				for _, rule := range localTLDsAndDotlessDomainsRules {

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Ensure every selectable variant/send-range draw is strictly longer than prefixLength
  2. For turn 0, keep minLength (and every variant total) >= firstTurnPrefixLength + 1
  3. Route writes through runPaddingSchedule so the up-front bounds check catches the misconfiguration deterministically

Example fix

// before: variant total 64 with prefix 64 leaves no record
paddingVariant{chunks: []int{64}}
// after
paddingVariant{chunks: []int{64, 256}}
Defensive patterns

Strategy: validation

Validate before calling

minSelectable := turn.minLength
if len(turn.variants) > 0 {
    minSelectable = math.MaxInt
    for _, v := range turn.variants {
        if l := paddingVariantLength(v); l < minSelectable {
            minSelectable = l
        }
    }
}
if minSelectable-prefixLength < 1 {
    return errors.New("turn cannot fit prefix plus a record")
}

Prevention

When it happens

Trigger: A variant on turn 0 whose chunk sum <= firstTurnPrefixLength, or a sendMinLength/sendMaxLength draw <= prefixLength. Note validation only guarantees minLength - prefix >= 1 for turn 0, so send-range draws below minLength (impossible after validation) or unvalidated direct writes trigger this.

Common situations: Calling writePaddingTurn directly with a short variant and a large prefix; a schedule where a variant equals the prefix length exactly; test scaffolding that passes prefixLength larger than the turn's target length.

Related errors


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