XTLS/Xray-core · error

failed to parse name server: {}

Error message

failed to parse name server: {}

What it means

Thrown by NameServerConfig.UnmarshalJSON in infra/conf/dns.go when the 'servers' entry is neither a plain address string nor a valid advanced nameserver object (the object form with address/subnet settings failed to unmarshal or validate). The raw JSON data is appended to the message.

Source

Thrown at infra/conf/dns.go:81

		c.Address = advanced.Address
		c.ClientIP = advanced.ClientIP
		c.Port = advanced.Port
		c.SkipFallback = advanced.SkipFallback
		c.Domains = advanced.Domains
		c.ExpectedIPs = advanced.ExpectedIPs
		c.ExpectIPs = advanced.ExpectIPs
		c.QueryStrategy = advanced.QueryStrategy
		c.Tag = advanced.Tag
		c.TimeoutMs = advanced.TimeoutMs
		c.DisableCache = advanced.DisableCache
		c.ServeStale = advanced.ServeStale
		c.ServeExpiredTTL = advanced.ServeExpiredTTL
		c.FinalQuery = advanced.FinalQuery
		c.UnexpectedIPs = advanced.UnexpectedIPs
		return nil
	}

	return errors.New("failed to parse name server: ", string(data))
}

func (c *NameServerConfig) Build() (*dns.NameServer, error) {
	if c.Address == nil {
		return nil, errors.New("nameserver address is not specified")
	}

	domainRules, err := geodata.ParseDomainRules(c.Domains, geodata.Domain_Substr)
	if err != nil {
		return nil, err
	}

	if len(c.ExpectedIPs) == 0 {
		c.ExpectedIPs = c.ExpectIPs
	}

	actPrior := false
	var newExpectedIPs StringList

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Use quoted strings: "servers": ["1.1.1.1", "8.8.8.8"]
  2. For object form always include a string address: {"address": "https://dns.google/dns-query", "domains": ["geosite:google"]}
  3. Check the appended raw data to find the failing entry

Example fix

// before
"servers": [{"adress": "1.1.1.1"}]

// after
"servers": [{"address": "1.1.1.1"}]
Defensive patterns

Strategy: validation

Validate before calling

func validateDNSServerEntry(v any) error {
    switch t := v.(type) {
    case string:
        if t == "" {
            return errors.New("empty nameserver string")
        }
        return nil
    case map[string]any:
        addr, ok := t["address"]
        if !ok {
            return errors.New("nameserver object missing 'address' key")
        }
        _, isStr := addr.(string)
        if !isStr {
            return errors.New("nameserver 'address' must be a string")
        }
        return nil
    }
    return errors.New("nameserver must be a string or object with string address")
}

Type guard

func isDNSServerEntry(v any) bool {
    if _, ok := v.(string); ok {
        return true
    }
    m, ok := v.(map[string]any)
    return ok && reflect.TypeOf(m["address"]).Kind() == reflect.String
}

Try / catch

if err := json.Unmarshal(entry, &nsc); err != nil {
    if strings.Contains(err.Error(), "failed to parse name server") {
        return fmt.Errorf("dns.servers entry %s: use a quoted string or {"address": "..."}", entry)
    }
    return err
}

Prevention

When it happens

Trigger: dns.servers entries like 8.8.8.8 (unquoted, invalid JSON or number), {"address": 123}, {"address": null}, an array ["a","b"] where one entry is a nested object, or an object missing a string 'address' field so the advanced-form branch fails.

Common situations: Mixing old-style string servers and new object form in one array incorrectly; forgetting quotes around DNS IPs with dots (usually a JSON syntax error caught earlier); typos in object keys so the address field stays empty.

Understand the failure class

Related errors


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