hatoo/oha · warning · anyhow::Error
DNS: failed to load /etc/resolv.conf
Error message
DNS: failed to load /etc/resolv.conf: {err} What it means
system_resolv_conf builds the DNS resolver configuration by reading the system's /etc/resolv.conf via hickory-resolver. If read_system_conf fails (file missing, unreadable, or unparseable — note Android/Termux is unsupported since hickory 0.26), the error is wrapped with the 'DNS: failed to load /etc/resolv.conf' prefix and passed to fallback_resolver_config, which may proceed with a fallback resolver or surface the error.
Solutions
- Ensure /etc/resolv.conf exists and is readable (e.g. create one with `nameserver 8.8.8.8` in containers).
- Fix file permissions on /etc/resolv.conf.
- On Android/Termux, note hickory 0.26 no longer supports it; run on a supported platform or supply a fallback config path.
- Inspect the wrapped `{err}` message for the underlying cause.
Example fix
// before # scratch container without resolv.conf oha https://example.com // after RUN echo 'nameserver 8.8.8.8' > /etc/resolv.conf oha https://example.com
Defensive patterns
Strategy: fallback
Validate before calling
use std::path::Path;
if !Path::new("/etc/resolv.conf").exists() {
eprintln!("Warning: /etc/resolv.conf missing; DNS resolution may fail");
} Try / catch
match system_resolv_conf() {
Ok((config, opts)) => /* build resolver */,
Err(e) if e.to_string().contains("failed to load /etc/resolv.conf") => {
// use a hardcoded ResolverConfig with public DNS, e.g. 8.8.8.8
}
Err(e) => return Err(e),
} Prevention
- Ensure base container images include /etc/resolv.conf (echo 'nameserver 8.8.8.8' > /etc/resolv.conf).
- Avoid Android/Termux targets with hickory-resolver >= 0.26.
- Keep resolv.conf readable and syntactically valid; check the wrapped {err} detail when it fires.
When it happens
Trigger: Container images or minimal systems without /etc/resolv.conf; restrictive file permissions on resolv.conf; running on Android/Termux where hickory 0.26+ removed that capability; a malformed resolv.conf that hickory cannot parse.
Common situations: Scratch/distroless Docker images lacking resolv.conf; sandboxed CI runners; Android environments after upgrading hickory-resolver.
Understand the failure class
Background: "Config file not found": what it means and how to fix it in docker-sync, Maven, Vagrant, Turborepo and other tools — this error's family across 60 libraries.
AI-assisted analysis of hatoo/oha@4efba2d113 (2026-09-09).
Data as JSON: /api/errors/e73a3cc9c38f8e9f.
Report an issue: GitHub.
Appendix: source
Thrown at src/main.rs:910
let (res, print_config) = data_collect_future.await;
printer::print_result(print_config, start, &res, duration)?;
if let Some(db_url) = opts.db_url {
eprintln!("Storing results to {db_url}");
db::store(&client, &db_url, start, res.success(), run as i64)?;
}
Ok(())
}
pub(crate) fn system_resolv_conf() -> anyhow::Result<(ResolverConfig, ResolverOpts)> {
// Termux (https://github.com/termux/termux-app) is no longer supported:
// hickory-resolver 0.26 removed `parse_resolv_conf` on Android, so we can
// no longer read `$PREFIX/etc/resolv.conf` via hickory.
match hickory_resolver::system_conf::read_system_conf() {
Ok(conf) => Ok(conf),
Err(err) => fallback_resolver_config(anyhow::anyhow!(
"DNS: failed to load /etc/resolv.conf: {err}"
)),
}
}
fn fallback_resolver_config(err: anyhow::Error) -> anyhow::Result<(ResolverConfig, ResolverOpts)> {
// Notify the user that we had to fall back to a default resolver configuration.
eprintln!("{err}");
let config = ResolverConfig::default();
let opts = ResolverOpts::default();
Ok((config, opts))
}
enum WorkMode {
Debug,
FixedNumber {
n_requests: usize,View on GitHub (pinned to 4efba2d113)