nautechsystems/nautilus_trader · error · anyhow::Error

Certificate path is not a directory: {}

Error message

Certificate path is not a directory: {}

What it means

create_tls_config_from_certs_dir expects certs_dir to be a directory containing certificate/key PEM files. If the path is not a directory (missing path, or a file passed instead), it bails with this error before scanning certificates.

Source

Thrown at crates/network/src/tls.rs:97

    match request.uri().host() {
        // rustls expects IPv6 addresses without the surrounding [] brackets
        Some(d) if d.starts_with('[') && d.ends_with(']') => Ok(d[1..d.len() - 1].to_string()),
        Some(d) => Ok(d.to_string()),
        None => Err(Error::Io(std::io::Error::new(
            std::io::ErrorKind::InvalidInput,
            "Request URI missing host component",
        ))),
    }
}

pub(crate) fn create_tls_config_from_certs_dir(
    certs_dir: &Path,
    require_client_auth: bool,
) -> anyhow::Result<rustls::ClientConfig> {
    install_cryptographic_provider();

    if !certs_dir.is_dir() {
        anyhow::bail!(
            "Certificate path is not a directory: {}",
            certs_dir.display()
        );
    }

    let mut all_certs: Vec<(std::path::PathBuf, Vec<CertificateDer<'static>>)> = Vec::new();
    let mut client_key = None;
    let mut root_store = rustls::RootCertStore::empty();
    root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());

    // Sort entries for deterministic cert/key selection across platforms
    let mut entries: Vec<_> = std::fs::read_dir(certs_dir)?.collect::<Result<Vec<_>, _>>()?;
    entries.sort_by_key(std::fs::DirEntry::path);

    for entry in entries {
        let path = entry.path();

        if client_key.is_none()

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Point the config at the directory that contains the PEM certs/keys, not a file inside it.
  2. Verify the path exists and is a directory at startup (certs_dir.is_dir()).
  3. Check volume mounts / file deployment so the certs directory is present in the runtime environment.
  4. Use absolute paths for cert directories to avoid working-directory surprises.

Example fix

// before
tls_certs_dir: "/etc/certs/ca.pem",
// after
tls_certs_dir: "/etc/certs",  // directory containing ca.pem, client.pem, client.key
Defensive patterns

Strategy: validation

Validate before calling

// Rust: validate TLS certs path at startup
let certs_dir = std::path::Path::new(&cfg.tls_certs_dir);
if !certs_dir.is_dir() {
    return Err(anyhow::anyhow!("tls_certs_dir must be a directory: {}", certs_dir.display()));
}

Type guard

fn is_certs_dir(p: &std::path::Path) -> bool { p.is_dir() }

Try / catch

let tls_cfg = create_tls_config_from_certs_dir(certs_dir, require_client_auth)
    .map_err(|e| { log::error!("tls config failed: {e}"); e })?;

Prevention

When it happens

Trigger: Calling create_tls_config_from_certs_dir (from connect_url with a TLS-enabled socket config) with a path that does not exist or points to a regular file instead of the certificates directory.

Common situations: TLS config pointing at the CA bundle file instead of the directory; typo'd or relative path resolved from the wrong working directory; deployment container missing the mounted certs volume; rename after a certs directory reorganization.

Understand the failure class

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/1ec6904bf75a869f. Report an issue: GitHub.