actix/actix-web · critical
Failed to find native root certificates
Error message
Failed to find native root certificates
What it means
This is a panic (via .expect) raised during awc Connector construction inside build_tls when the `rustls-0_23-native-roots` Cargo feature is enabled. The code calls `rustls_0_23::native_roots_cert_store().expect("Failed to find native root certificates")` (connector.rs:122), which reads the operating system's trust store. If that load returns an error (no readable CA bundle), the .expect panics. The API doc on Connector::new (connector.rs:93-94) explicitly warns: when rustls-0_23-native-roots is enabled and the runtime system has no native root certificates, this method will panic.
Solutions
- Install the OS CA bundle in the container: on Debian/Ubuntu add `ca-certificates`; on Alpine add `apk add --no-cache ca-certificates` (and update-ca-certificates). This makes native_roots_cert_store succeed.
- Switch the awc Cargo feature from `rustls-0_23-native-roots` to `rustls-0_23-webpki-roots`, which bundles Mozilla's root set statically and never reads the OS store (no runtime dependency).
- Provide your own rustls ClientConfig with an explicit root store and pass it via `awc::Connector::new().rustls_0_23(Arc::new(config))`, bypassing the native-roots code path entirely.
- If you only need plaintext HTTP or manage TLS separately, avoid enabling any native-roots feature so build_tls uses a non-native path.
- Verify the image is not distroless/scratch; if it must be, prefer webpki-roots (solution 2) rather than shipping a CA bundle.
Example fix
# before (awc/Cargo.toml or downstream Cargo.toml)
awc = { version = "4", features = ["rustls-0_23-native-roots"] }
# after: use bundled webpki roots (no OS cert store required)
awc = { version = "4", features = ["rustls-0_23-webpki-roots"] }
# alternative: keep native-roots but fix the Dockerfile (Alpine)
# RUN apk add --no-cache ca-certificates && update-ca-certificates Defensive patterns
Strategy: fallback
Validate before calling
// Run before constructing the awc Connector to detect a missing OS trust store
// on Linux. native_roots_cert_store reads /etc/ssl/certs (and platform paths).
use std::path::Path;
fn os_certs_available() -> bool {
// Common Linux locations probed by rustls-native-certs
["/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu
"/etc/pki/tls/certs/ca-bundle.crt", // RHEL/Fedora
"/etc/ssl/cert.pem"] // Alpine/macOS
.iter()
.any(|p| {
let path = Path::new(p);
path.exists() && path.metadata().map(|m| m.len() > 0).unwrap_or(false)
})
}
if !os_certs_available() {
panic!("no native root certificates; install ca-certificates or use webpki-roots");
} Type guard
// No type-level distinction: a feature flag selects native vs webpki roots at // compile time, and the panic is runtime. Use a build/runtime check instead. // null
Try / catch
// Connector::new() panics (not returns Err), so isolate it with catch_unwind
// when you cannot guarantee the OS trust store, then fall back to a manually
// configured rustls connector:
use std::panic;
let connector = match panic::catch_unwind(|| awc::Connector::new()) {
Ok(c) => c,
Err(_) => {
// Build a rustls config from bundled webpki roots instead
use actix_tls::connect::rustls_0_23::webpki_roots_cert_store;
use rustls::ClientConfig;
let config = ClientConfig::builder()
.with_root_certificates(webpki_roots_cert_store())
.with_no_client_auth();
awc::Connector::new().rustls_0_23(std::sync::Arc::new(config))
}
};
let client = awc::Client::builder().connector(connector).finish(); Prevention
- For containerized deployments prefer the `rustls-0_23-webpki-roots` feature over native-roots to remove the OS dependency entirely.
- If you must use native-roots, install `ca-certificates` in your Dockerfile and run update-ca-certificates for Alpine.
- Add a startup self-check (see validationCode) that fails fast with a clear message instead of panicking deep in awc.
- Keep TLS feature selection in a single, documented place in Cargo.toml so it is reviewable per deployment target.
- Smoke-test the client in CI using the same image that runs in production to catch missing trust stores before release.
When it happens
Trigger: Enabling awc's `rustls-0_23-native-roots` feature and constructing a client/connector (`awc::Client::new()`, `awc::Connector::new()`, or anything that builds the default TLS connector) on a system whose native root certificate store cannot be read. The panic fires at startup, on the first construction of Connector (during build_tls -> native_roots_cert_store), before any network call is attempted.
Common situations: Running in minimal Docker images such as `scratch`, `distroless`, or Alpine Linux without the ca-certificates package installed; containers where /etc/ssl/certs/ca-certificates.crt is absent or empty; stripped CI images; cross-compilation targets or embedded environments without an OS trust store; Windows/macOS dev machines are usually fine, but Linux containers frequently lack the bundle.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- failed to build Hickory DNS resolver
- actix-http client only supports versions http/1.1 & http/2
- Response Payload IO timed out
AI-assisted analysis of actix/actix-web@4d435abc28 (2026-08-09).
Data as JSON: /api/errors/88684d446ec1ddf9.
Report an issue: GitHub.
Appendix: source
Thrown at awc/src/client/connector.rs:122
connector: TcpConnector::new(resolver::resolver()).service(),
config: ConnectorConfig::default(),
tls: Self::build_tls(vec![b"h2".to_vec(), b"http/1.1".to_vec()]),
}
}
cfg_if::cfg_if! {
if #[cfg(any(feature = "rustls-0_23-webpki-roots", feature = "rustls-0_23-native-roots"))] {
/// Build TLS connector with Rustls v0.23, based on supplied ALPN protocols.
///
/// Note that if other TLS crate features are enabled, Rustls v0.23 will be used.
fn build_tls(protocols: Vec<Vec<u8>>) -> OurTlsConnector {
use actix_tls::connect::rustls_0_23::{self, reexports::ClientConfig};
cfg_if::cfg_if! {
if #[cfg(feature = "rustls-0_23-webpki-roots")] {
let certs = rustls_0_23::webpki_roots_cert_store();
} else if #[cfg(feature = "rustls-0_23-native-roots")] {
let certs = rustls_0_23::native_roots_cert_store().expect("Failed to find native root certificates");
}
}
let mut config = ClientConfig::builder()
.with_root_certificates(certs)
.with_no_client_auth();
config.alpn_protocols = protocols;
OurTlsConnector::Rustls023(std::sync::Arc::new(config))
}
} else if #[cfg(any(feature = "rustls-0_22-webpki-roots", feature = "rustls-0_22-native-roots"))] {
/// Build TLS connector with Rustls v0.22, based on supplied ALPN protocols.
fn build_tls(protocols: Vec<Vec<u8>>) -> OurTlsConnector {
use actix_tls::connect::rustls_0_22::{self, reexports::ClientConfig};
cfg_if::cfg_if! {
if #[cfg(feature = "rustls-0_22-webpki-roots")] {View on GitHub (pinned to 4d435abc28)