XTLS/Xray-core · error

FakeDNSEngine is not initialized, but such a sniffer is used

Error message

FakeDNSEngine is not initialized, but such a sniffer is used

What it means

The first padding turn must carry the protocol prefix (e.g. an early data / auth tag) followed by at least one byte of real padding record, so validatePaddingSchedule requires schedule[0]'s minimum total length to exceed firstTurnPrefixLength. If minLength - firstTurnPrefixLength < 1, the smallest possible turn cannot hold even one record byte and the schedule is rejected.

Source

Thrown at app/dispatcher/fakednssniffer.go:26

	"github.com/xtls/xray-core/common/errors"
	"github.com/xtls/xray-core/common/net"
	"github.com/xtls/xray-core/common/session"
	"github.com/xtls/xray-core/core"
	"github.com/xtls/xray-core/features/dns"
)

// newFakeDNSSniffer Creates a Fake DNS metadata sniffer
func newFakeDNSSniffer(ctx context.Context) (protocolSnifferWithMetadata, error) {
	var fakeDNSEngine dns.FakeDNSEngine
	{
		fakeDNSEngineFeat := core.MustFromContext(ctx).GetFeature((*dns.FakeDNSEngine)(nil))
		if fakeDNSEngineFeat != nil {
			fakeDNSEngine = fakeDNSEngineFeat.(dns.FakeDNSEngine)
		}
	}

	if fakeDNSEngine == nil {
		errNotInit := errors.New("FakeDNSEngine is not initialized, but such a sniffer is used").AtError()
		return protocolSnifferWithMetadata{}, errNotInit
	}
	return protocolSnifferWithMetadata{protocolSniffer: func(ctx context.Context, bytes []byte) (SniffResult, error) {
		outbounds := session.OutboundsFromContext(ctx)
		ob := outbounds[len(outbounds)-1]
		if ob.Target.Network == net.Network_TCP || ob.Target.Network == net.Network_UDP {
			domainFromFakeDNS := fakeDNSEngine.GetDomainFromFakeDNS(ob.Target.Address)
			if domainFromFakeDNS != "" {
				errors.LogInfo(ctx, "fake dns got domain: ", domainFromFakeDNS, " for ip: ", ob.Target.Address.String())
				return &fakeDNSSniffResult{domainName: domainFromFakeDNS}, nil
			}
		}

		if ipAddressInRangeValueI := ctx.Value(ipAddressInRange); ipAddressInRangeValueI != nil {
			ipAddressInRangeValue := ipAddressInRangeValueI.(*ipAddressInRangeOpt)
			if fkr0, ok := fakeDNSEngine.(dns.FakeDNSEngineRev0); ok {
				inPool := fkr0.IsIPInIPPool(ob.Target.Address)
				ipAddressInRangeValue.addressInRange = &inPool

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Increase turn 0 minLength so it is at least firstTurnPrefixLength + 1
  2. Or reduce firstTurnPrefixLength (e.g. smaller/no early data) if the prefix is under your control
  3. Verify both peers use matching prefix expectations for the first turn

Example fix

// before
runPaddingSchedule(r, w, true, 64, []paddingTurn{{
  direction: paddingClientToServer, minLength: 64, maxLength: 128,
}})
// after
runPaddingSchedule(r, w, true, 64, []paddingTurn{{
  direction: paddingClientToServer, minLength: 65, maxLength: 128,
}})
Defensive patterns

Strategy: validation

Validate before calling

if len(schedule) > 0 && schedule[0].minLength-firstTurnPrefixLength < 1 {
    return fmt.Errorf("first turn must exceed prefix %d", firstTurnPrefixLength)
}

Prevention

When it happens

Trigger: firstTurnPrefixLength >= minLength of turn 0 (e.g. prefix 100 with minLength 100 or 99). Triggered by runPaddingSchedule with a non-zero prefix and a short first turn; fails during up-front validation.

Common situations: Enabling 0-RTT / early data (which raises the prefix length) without enlarging the first turn's minLength; hand-tuning minLength down for smaller overhead; a server profile with a large prefix paired with a client's short-turn schedule.

Related errors


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