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

  1. 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.
  2. 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).
  3. 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.
  4. If you only need plaintext HTTP or manage TLS separately, avoid enabling any native-roots feature so build_tls uses a non-native path.
  5. 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

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

Related errors


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)