shadowsocks/shadowsocks-rust · error

initialize DNS resolver with config failed, error: {}

Error message

initialize DNS resolver with config failed, error: {}

What it means

create_resolver builds a hickory-dns (trust-dns) resolver from explicit name-server config; when builder.build() fails (e.g. no valid name servers after filtering, invalid configuration), the error is logged and re-wrapped as io::ErrorKind::Other. Message text is "initialize DNS resolver with config failed, error: {inner}".

Source

Thrown at crates/shadowsocks/src/dns_resolver/hickory_dns_resolver.rs:164

            if let Some(opts) = opts {
                *builder.options_mut() = opts;
            }
            let resolver_opts = builder.options_mut();

            // Use Ipv4AndIpv6 strategy. Because Ipv4ThenIpv6 or Ipv6ThenIpv4 will return if the first query returned.
            // Since we want to use Happy Eyeballs to connect to both IPv4 and IPv6 addresses, we need both A and AAAA records.
            resolver_opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;

            // Enable EDNS0 for large records
            resolver_opts.edns0 = true;

            trace!("initializing DNS resolver with opts {:?}", resolver_opts);

            match builder.build() {
                Ok(resolver) => Ok(resolver),
                Err(err) => {
                    error!("initialize DNS resolver with config failed, error: {}", err);
                    Err(io::Error::new(io::ErrorKind::Other, err))
                }
            }
        }

        // To make this independent, if targeting macOS, BSD, Linux, or Windows, we can use the system's configuration
        // Android doesn't have /etc/resolv.conf.
        None => {
            match DnsResolver::builder(ShadowDnsRuntimeProvider::new(connect_opts)) {
                Ok(mut builder) => {
                    let opts = builder.options_mut();
                    // NOTE: timeout will be set by config (for example, /etc/resolv.conf on UNIX-like system)
                    //
                    // Only ip_strategy should be changed. Why Ipv4AndIpv6? See comments above.
                    opts.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;

                    // Enable EDNS0 for large records
                    opts.edns0 = true;

View on GitHub (pinned to 8eb0f0a65b)

Solutions

  1. Check the inner error in the log line to see the underlying hickory failure
  2. Ensure at least one valid, reachable nameserver IP:port is present in the DNS config
  3. Fix malformed nameserver entries (valid IPs, port 53/853) in the configuration
  4. Enable system-config resolution instead of manual config if /etc/resolv.conf or OS settings are usable

Example fix

// before
ns: ["999.999.1.1:53"]
// after
ns: ["8.8.8.8:53", "1.1.1.1:53"]
Defensive patterns

Strategy: fallback

Validate before calling

let ns: Vec<_> = cfg.nameservers.iter().filter(|n| n.parse::<std::net::SocketAddr>().is_ok()).collect();
if ns.is_empty() { return Err(anyhow!("no valid nameservers configured")); }

Try / catch

match create_resolver(opts).await {
    Err(e) if e.kind() == io::ErrorKind::Other => {
        log::error!("resolver init failed: {e}; falling back to public DNS");
        create_resolver_with_fallback_dns().await?
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling hickory_resolver / hickory_dns_system_resolver / hickory_dns_notify_update_dns where the explicitly provided nameserver config (ips, ports, trust anchors, opts) fails hickory's build validation — commonly zero usable name servers or invalid server addresses.

Common situations: Config file with malformed or empty nameservers list; all nameservers filtered out (e.g. IPv6 servers on IPv4-only host); invalid DNS port or bogus IP strings in dns config.

Related errors


AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09). Data as JSON: /api/errors/7b49302a421871cf. Report an issue: GitHub.