XTLS/Xray-core · error

invalid DNS hosts

Error message

invalid DNS hosts

What it means

Thrown by HostsWrapper.UnmarshalJSON in infra/conf/dns.go when the dns.hosts value as a whole is not a JSON object mapping hostnames to HostAddress values. json.Unmarshal into map[string]*HostAddress must succeed; otherwise the error is returned with the json error chained via .Base(err).

Source

Thrown at infra/conf/dns.go:250

	return &dns.Config_HostMapping{
		Ip: ips,
	}
}

// MarshalJSON implements encoding/json.Marshaler.MarshalJSON
func (m *HostsWrapper) MarshalJSON() ([]byte, error) {
	return json.Marshal(m.Hosts)
}

// UnmarshalJSON implements encoding/json.Unmarshaler.UnmarshalJSON
func (m *HostsWrapper) UnmarshalJSON(data []byte) error {
	hosts := make(map[string]*HostAddress)
	err := json.Unmarshal(data, &hosts)
	if err == nil {
		m.Hosts = hosts
		return nil
	}
	return errors.New("invalid DNS hosts").Base(err)
}

// Build implements Buildable
func (m *HostsWrapper) Build() ([]*dns.Config_HostMapping, error) {
	mappings := make([]*dns.Config_HostMapping, 0, len(m.Hosts))
	for rule, addrs := range m.Hosts {
		mapping := newHostMapping(addrs)
		rule, err := geodata.ParseDomainRule(rule, geodata.Domain_Full)
		if err != nil {
			return nil, err
		}
		mapping.Domain = rule
		mappings = append(mappings, mapping)
	}
	return mappings, nil
}

// Build implements Buildable

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Make hosts a flat object: "hosts": {"domain.example": "1.2.3.4"}
  2. Run the config through a JSON validator to catch structural syntax errors
  3. Inspect the chained base error for the exact JSON position

Example fix

// before
"hosts": ["example.com"]

// after
"hosts": {"example.com": "1.2.3.4"}
Defensive patterns

Strategy: type-guard

Validate before calling

func validateHostsShape(v any) error {
    m, ok := v.(map[string]any)
    if !ok {
        return errors.New("hosts must be an object mapping domains to addresses")
    }
    for k, val := range m {
        if err := isHostAddressValue(val); err != nil {
            return fmt.Errorf("hosts[%s]: %w", k, err)
        }
    }
    return nil
}

Type guard

func isHostsMap(v any) bool {
    m, ok := v.(map[string]any)
    if !ok {
        return false
    }
    for _, val := range m {
        if !isHostAddress(val) {
            return false
        }
    }
    return true
}

Try / catch

if err := json.Unmarshal(data, &hw); err != nil {
    if strings.Contains(err.Error(), "invalid DNS hosts") {
        return fmt.Errorf("dns.hosts must be an object like {"example.com": "1.2.3.4"}")
    }
    return err
}

Prevention

When it happens

Trigger: "hosts": ["example.com"], "hosts": "example.com", "hosts": 123, or a JSON object whose keys are fine but syntax errors exist (missing quotes, trailing comma) making the map unmarshal fail.

Common situations: Treating hosts like an array of domain strings; YAML-to-JSON converters emitting null for an empty hosts map in some edge cases; per-entry problems surface as error 254 instead, so this one indicates a structural (map-level) problem.

Related errors


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