XTLS/Xray-core · error
invalid fakeDNS setting
Error message
invalid fakeDNS setting
What it means
The first chunk of a padding record must carry the varint length header (1-5 bytes), so writePaddingTurnWithBuffer requires chunks[0] >= header.Len(). With a generated (range-based) turn, defaultPaddingChunks sizes the first chunk at min(recordLength, writeChunkLength) which is always >= recordLength's own varint size, so this fires only with variant-based turns whose first post-prefix chunk is smaller than the encoded header.
Source
Thrown at app/dns/fakedns/fake.go:48
}
func (fkdns *Holder) GetFakeIPForDomain3(domain string, ipv4, ipv6 bool) []net.Address {
isIPv6 := fkdns.ipRange.IP.To4() == nil
if (isIPv6 && ipv6) || (!isIPv6 && ipv4) {
return fkdns.GetFakeIPForDomain(domain)
}
return []net.Address{}
}
func (*Holder) Type() interface{} {
return (*dns.FakeDNSEngine)(nil)
}
func (fkdns *Holder) Start() error {
if fkdns.config != nil && fkdns.config.IpPool != "" && fkdns.config.LruSize != 0 {
return fkdns.initializeFromConfig()
}
return errors.New("invalid fakeDNS setting")
}
func (fkdns *Holder) Close() error {
// nothing to do for now, just wait GC
return nil
}
func NewFakeDNSHolder() (*Holder, error) {
var fkdns *Holder
var err error
if fkdns, err = NewFakeDNSHolderConfigOnly(nil); err != nil {
return nil, errors.New("Unable to create Fake Dns Engine").Base(err).AtError()
}
err = fkdns.initialize(dns.FakeIPv4Pool, 65535)
if err != nil {
return nil, err
}View on GitHub (pinned to 7d214f8b09)
Solutions
- Enlarge the variant's first post-prefix chunk to at least 5 bytes (safe upper bound for any varint)
- Or reorder chunks so a chunk >= the encoded record length's varint size comes first
- Recompute after any prefix-length change, since trimming shifts which chunk is first
Example fix
// before: record ~300 bytes needs a 2-byte header, first chunk is 1
paddingVariant{chunks: []int{100, 1, 200}}
// after
paddingVariant{chunks: []int{100, 8, 193}} Defensive patterns
Strategy: validation
Validate before calling
func firstChunkFitsHeader(v paddingVariant, prefix int) bool {
chunks, _, err := trimPaddingPrefix(v, prefix)
if err != nil || len(chunks) == 0 {
return false
}
total := 0
for _, c := range chunks {
total += c
}
return chunks[0] >= len(Varint(total - 0).bytesIfAvailable()) // fallback: chunks[0] >= 5 is always safe
} Prevention
- Make the first post-prefix chunk at least 5 bytes (max varint size) in every variant
- Recheck after prefix changes since trimming changes which chunk is first
When it happens
Trigger: A variant whose first chunk after prefix trimming is 1 byte while recordLength >= 128 (2-byte varint), or generally first chunk < varint length of the remaining record. Detected at write time, after validation has passed (validation checks chunk bounds but not header fit).
Common situations: Traffic-shaping variants crafted with a small leading chunk to mimic a specific protocol's first segment; prefix trimming that consumes large leading chunks and exposes a tiny next chunk.
Related errors
- Failed to convert address to Net IP.
- failed to create hosts
- tries to resolve itself!
- not a Service.
- Dispatcher: Invalid destination.
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/789a9aa6589bf745.
Report an issue: GitHub.