XTLS/Xray-core · info · errors.Error
empty domain name
Error message
empty domain name
What it means
rejectNonIPQuery builds a DNS response for non-A/AAAA queries, but first requires a non-empty QNAME: after trimming the trailing root dot, the domain must not be empty. A query for the root ("." or "") fails this check, so the handler cannot construct a valid reply message and returns this error.
Source
Thrown at proxy/dns/dns.go:396
}
msgBytes, err := builder.Finish()
if err != nil {
errors.LogInfoInner(context.Background(), err, "pack message")
b.Release()
timer.SetTimeout(0)
}
b.Resize(0, int32(len(msgBytes)))
if err := writer.WriteMessage(b); err != nil {
errors.LogInfoInner(context.Background(), err, "write IP answer")
timer.SetTimeout(0)
}
}
func (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter, rCode dnsmessage.RCode) error {
domainT := strings.TrimSuffix(domain, ".")
if domainT == "" {
return errors.New("empty domain name")
}
b := buf.New()
rawBytes := b.Extend(buf.Size)
builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{
ID: id,
RCode: rCode,
RecursionAvailable: true,
RecursionDesired: true,
Response: true,
Authoritative: true,
})
builder.EnableCompression()
common.Must(builder.StartQuestions())
err := builder.Question(dnsmessage.Question{
Name: dnsmessage.MustNewName(domain),
Class: dnsmessage.ClassINET,
Type: qType,
})View on GitHub (pinned to 7d214f8b09)
Solutions
- Identify the client issuing root-label queries (packet capture, query logging) — often a probe or scanner
- If the traffic is unwanted, block it upstream of Xray or with a pre-rule (e.g. drop before dns outbound)
- No config change inside Xray fixes it: the dns outbound simply cannot answer root queries; treat as expected behavior
- Update/patch clients that query the root through the proxy
Example fix
// example: guard in your own handler before delegating to dns outbound
// before
_ = dnsOut.Process(ctx, link, dialer)
// after
if q.Name == "." || strings.TrimSuffix(q.Name, ".") == "" {
return sendRefused(id, q.Type, writer)
}
_ = dnsOut.Process(ctx, link, dialer) Defensive patterns
Strategy: validation
Validate before calling
qname := strings.TrimSuffix(string(q.Name), ".")
if qname == "" { sendRefusedAndReturn() } // guard before routing to dns outbound Try / catch
if err := handler.RejectNonIP(id, qType, domain, w, rcode); err != nil {
if err.Error() == "empty domain name" { dropPacket(); return }
return err
} Prevention
- Filter root-label (".") DNS queries upstream or with pre-rules
- Treat this error as expected for scanners/probes; alert only on spikes
- Keep DNS query logging on to attribute root queries to clients
When it happens
Trigger: A DNS query with QNAME "." (root NS/SOA queries) or an empty QNAME reaching the dns outbound and being classified as a non-IP query (not TypeA/TypeAAAA).
Common situations: Recursive resolvers or monitoring probes issuing root NS / . IN SOA queries; some DNS-SD/LLMNR traffic; usually an edge case hit by scanners rather than normal clients.
Related errors
- failed to resolve ip for target ${domain}
- unknown action: {}
- rCode out of range: {}
- legacy nonIPQuery and blockTypes cannot be mixed with rules
- unknown nonIPQuery: {}
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/375ca322555e2484.
Report an issue: GitHub.