stalwartlabs/stalwart · critical
Failed to build DNSSEC resolver
Error message
Failed to build DNSSEC resolver
What it means
This panic occurs during resolver initialization when the DNSSEC-validating hickory (trust-dns) resolver cannot be built from the provided ResolverConfig and options. DnssecResolver::new calls .build().expect(...), so any builder failure aborts startup instead of returning a typed error. It means the DNSSEC resolver could not be constructed at all from the given configuration.
Source
Thrown at crates/common/src/config/smtp/resolver.rs:225
}
}
// We already have a cache, so disable the built-in cache
opts.cache_size = 0;
// Prepare DNSSEC resolver options
let config_dnssec = resolver_config.clone();
let mut opts_dnssec = opts.clone();
opts_dnssec.validate = true;
let dnssec = DnssecResolver {
resolver: TokioResolver::builder_with_config(
config_dnssec,
TokioRuntimeProvider::default(),
)
.with_options(opts_dnssec)
.build()
.expect("Failed to build DNSSEC resolver"),
};
Resolvers {
#[cfg(not(feature = "test_mode"))]
dnssec_available: ensure_dnssec(&resolver_config, &dnssec.resolver).await,
#[cfg(feature = "test_mode")]
dnssec_available: true,
dns: MessageAuthenticator::new(resolver_config, opts).unwrap(),
dnssec,
}
}
}
#[cfg(not(feature = "test_mode"))]
async fn ensure_dnssec(config: &ResolverConfig, resolver: &TokioResolver) -> bool {
config.name_servers().iter().any(|name_server| {
name_server
.connectionsView on GitHub (pinned to e962003857)
Solutions
- Check that the resolver nameserver IPs and ports in the SMTP resolver config are valid (dotted-quad, valid port)
- Remove or fix any custom DNSSEC trust-anchor/root-key settings in the configuration
- Test with default system resolvers to isolate whether the custom config is the cause
- Verify hickory-resolver version compatibility after dependency upgrades
- Replace .expect() with error propagation at the call site so the underlying builder error is reported instead of an opaque panic
Example fix
// before
.build()
.expect("Failed to build DNSSEC resolver"),
// after
.build()
.map_err(|e| anyhow!("Failed to build DNSSEC resolver: {e}"))?, Defensive patterns
Strategy: validation
Validate before calling
// validate resolver config before init
fn validate_resolver_config(cfg: &ResolverConfig) -> Result<(), String> {
for ns in cfg.name_servers() {
// skip_addr parses the IP:port of each nameserver
if ns.addr().is_unspecified() {
return Err(format!("invalid nameserver address: {}", ns));
}
}
Ok(())
} Try / catch
// .expect() panics; wrap process startup to surface the message:
match resolvers_init(&config).await {
Ok(r) => r,
Err(e) => {
eprintln!("resolver init failed: {e}");
std::process::exit(1);
}
} Prevention
- Validate nameserver IPs/ports in smtp.resolver config before startup
- Avoid custom DNSSEC trust anchors unless you can verify their format
- Test resolver init with default system configuration as a baseline
- Keep hickory-resolver versions aligned across the workspace after upgrades
When it happens
Trigger: Calling DnssecResolver::new (as part of Resolvers init) where TokioResolver::builder_with_config(config_dnssec, TokioRuntimeProvider::default()).with_options(opts_dnssec).build() returns an error — e.g. a ResolverConfig containing unparseable nameserver IPs or invalid DNSSEC options/trust anchors.
Common situations: smtp.resolver config with malformed nameserver IPs or ports; invalid custom DNSSEC trust-anchor/root key settings; hickory-resolver version incompatibility after dependency upgrades; platform/network init failures inside hickory.
Related errors
- Failed to build DNS resolver
- Failed to parse calendar template
- Node id {node_id} exceeds {MAX_NODE_ID}, panicking to avoid
- Invalid system time, panicking to avoid data corruption
- unwrap_tls called on non-TLS acceptor
AI-assisted analysis of stalwartlabs/stalwart@e962003857 (2026-09-06).
Data as JSON: /api/errors/876b003b27526e44.
Report an issue: GitHub.