actix/actix-web · critical

Failed to find native root certificates

Error message

Failed to find native root certificates

What it means

Raised by `.expect("Failed to find native root certificates")` at awc/src/client/connector.rs:122 inside `build_tls`, which only compiles when the `rustls-0_23-native-roots` Cargo feature is on. On `Connector` construction (including via `Client::default()`/`Client::builder().finish()`) the library calls `rustls_0_23::native_roots_cert_store()`, which uses the `rustls-native-certs` crate to read the host OS trust store (macOS Keychain, Windows cert store, or `/etc/ssl/certs` on Linux). If that read returns `Err`, `.expect()` panics, crashing the process at startup with no TLS possible.

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 937960ca67)

Solutions

  1. Install the OS CA bundle: Debian/Ubuntu `apt-get update && apt-get install -y ca-certificates`; Alpine `apk add --no-cache ca-certificates`; Fedora/RHEL `dnf install -y ca-certificates`.
  2. Switch Cargo features from `rustls-0_23-native-roots` to `rustls-0_23-webpki-roots`, which embeds the Mozilla root program at compile time and removes any runtime OS dependency (best for containers/cross-compile).
  3. If you must use native roots, point `SSL_CERT_FILE` (e.g. `/etc/ssl/certs/ca-certificates.crt`) or `SSL_CERT_DIR` at a valid PEM bundle the process can read.
  4. Verify the bundle is readable by the runtime user (`ls -l /etc/ssl/certs/ca-certificates.crt`) and run `update-ca-certificates` / `ca-certificates update` to regenerate it.

Example fix

# before (awc/Cargo.toml features)
awc = { features = ["rustls-0_23-native-roots"] }

# after - use bundled Mozilla roots, no OS store dependency
awc = { features = ["rustls-0_23-webpki-roots"] }

# or, in a Dockerfile, ensure the bundle is present
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/*
Defensive patterns

Strategy: validation

Validate before calling

// Run before constructing an awc::Connector with rustls-0_23-native-roots.
fn native_roots_available() -> bool {
    #[cfg(target_os = "linux")]
    {
        let candidates = [
            std::env::var("SSL_CERT_FILE").unwrap_or_default(),
            "/etc/ssl/certs/ca-certificates.crt".into(),
            "/etc/pki/tls/certs/ca-bundle.crt".into(),
        ];
        candidates.iter().any(|p| !p.is_empty() && std::path::Path::new(p).exists())
    }
    #[cfg(not(target_os = "linux"))]
    { true } // Keychain / Windows store; assume present
}

if !native_roots_available() {
    panic!("Refusing to build awc::Connector: no native CA bundle found. Install ca-certificates or enable rustls-0_23-webpki-roots.");
}

Try / catch

// The error is a panic via .expect(), so std::try-catch cannot intercept it.
// Best defense is validation + feature fallback. As a last-resort guard you can
// catch_unwind, but prefer fixing the environment:
use std::panic;
let connector = panic::catch_unwind(|| awc::Connector::new());
match connector {
    Ok(c) => c,
    Err(_) => { /* log and exit; fix the CA bundle or switch features */ }
}

Prevention

When it happens

Trigger: Constructing an `awc::Connector` or `awc::Client` while the crate was compiled with the `rustls-0_23-native-roots` feature, on a host whose system certificate store returns an error when `rustls-native-certs` reads it (empty result, unreadable bundle, or unsupported platform). The panic fires synchronously during the `Connector::new()` / `build_tls` call, before any request is sent.

Common situations: Minimal Docker images (alpine, distroless, `scratch`) that ship without a CA bundle; CI runners with stripped/missing `/etc/ssl/certs/ca-certificates.crt`; locked-down environments where the cert files exist but are not readable by the process user; cross-compilation targets where native cert loading is unsupported; systems where `ca-certificates` was never installed or was purged.

Related errors


AI-assisted analysis of actix/actix-web@937960ca67 (2026-08-06). Data as JSON: /data/errors/88684d446ec1ddf9.json. Report an issue: GitHub.