astrid-runtime/astrid · error · std::io::Error::NotFound
host did not resolve to any address
Error message
host did not resolve to any address
What it means
SafeDnsResolver::resolve completed the DNS lookup but produced an empty address set, so there is no address to connect to. The resolver reports this as an io::Error with ErrorKind::NotFound (which the caller maps to the typed `dns-error`) and sets `dns_failed` — explicitly not an SSRF block, just an ordinary resolution miss. It is surfaced to guests as 'DNS could not resolve the hostname'.
Source
Thrown at crates/astrid-capsule/src/engine/wasm/host/http/ssrf.rs:98
let (safe_addrs, saw_unsafe) = filter_safe_addrs(addrs, exempt);
if safe_addrs.is_empty() {
// All resolved addresses failed the airlock: a genuine SSRF
// block. Mark `tripped` so the caller can emit the typed
// `airlock-rejected` instead of a generic connection error.
if saw_unsafe {
tripped.store(true, Ordering::Relaxed);
return Err(Box::new(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"DNS resolved to an unauthorized private or local IP address",
))
as Box<dyn std::error::Error + Send + Sync>);
}
// Resolved to an empty address set: an ordinary resolution miss,
// not an airlock block — mark `dns_failed`, not `tripped`.
dns_failed.store(true, Ordering::Relaxed);
return Err(Box::new(std::io::Error::new(
std::io::ErrorKind::NotFound,
"host did not resolve to any address",
))
as Box<dyn std::error::Error + Send + Sync>);
}
let iter: reqwest::dns::Addrs = Box::new(safe_addrs.into_iter());
Ok(iter)
})
}
}
/// Partition resolved addresses into the airlock-safe set, reporting
/// whether any address was dropped as unsafe. An all-unsafe result (empty
/// safe set with `saw_unsafe == true`) is an airlock rejection; an empty
/// input is an ordinary resolution miss.
///
/// `exempt` (the operator allowlist matched this host:port at pre-flight)View on GitHub (pinned to affd8760f4)
Solutions
- Fix the hostname in the guest's request (check spelling and that the DNS record exists via an external resolver)
- Verify the target service is still deployed and its DNS record (A/AAAA) is published in the resolver the sandbox uses
- Retry after DNS propagation if the record was just created (new domains can take time to propagate)
- If the name only exists on the operator's network, use the exempt/allowlisted host path or expose a public endpoint
Example fix
// before: stale hostname in guest config let base = "https://api-old.internal.example.com"; // record deleted // after: current published hostname let base = "https://api.internal.example.com";
Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight check that the host resolves to at least one address:
fn host_resolves(host: &str) -> bool {
tokio::net::lookup_host((host, 0u16))
.map(|mut addrs| addrs.next().is_some())
.unwrap_or(false)
} Try / catch
match result {
Err(e) if e.kind() == std::io::ErrorKind::NotFound
|| e.to_string().contains("did not resolve") => {
eprintln!("DNS miss for host; retrying with backoff");
retry_with_backoff(host, 3);
}
other => propagate(other),
} Prevention
- Validate hostnames in guest configuration against DNS before deployment
- Retry with exponential backoff for freshly created DNS records (propagation delay)
- Monitor service decommissioning: remove or update guest configs when a DNS record is deleted
- Prefer stable service endpoints over ad-hoc internal names in guest code
When it happens
Trigger: A guest requests a URL whose hostname resolves to zero addresses: newly registered or deleted DNS records, a host removed from internal DNS, IPv6-only/AAAA-only environments where lookup yields no usable addresses for the requested family, or empty stub-zone answers.
Common situations: Typo'd or decommissioned hostname in guest configuration; DNS TTL expiry after a service was torn down; split-horizon DNS where the sandbox's resolver has no record for an internal name; a service's DNS record removed during a deploy while guest code still calls it.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- DNS resolved to an unauthorized private or local IP address
- source path does not exist: {source}
- no GitHub release found for version {version} in {org}/{repo
- no GitHub releases found for {org}/{repo}
- GitHub API rate limit exceeded - try again later
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/806f9cd2f9689023.
Report an issue: GitHub.