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}".

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.

Appendix: 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)