{"record":{"id":"1ec6904bf75a869f","repo":"nautechsystems/nautilus_trader","slug":"certificate-path-is-not-a-directory","errorCode":null,"errorMessage":"Certificate path is not a directory: {}","messagePattern":"Certificate path is not a directory: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/network/src/tls.rs","lineNumber":97,"sourceCode":"    match request.uri().host() {\n        // rustls expects IPv6 addresses without the surrounding [] brackets\n        Some(d) if d.starts_with('[') && d.ends_with(']') => Ok(d[1..d.len() - 1].to_string()),\n        Some(d) => Ok(d.to_string()),\n        None => Err(Error::Io(std::io::Error::new(\n            std::io::ErrorKind::InvalidInput,\n            \"Request URI missing host component\",\n        ))),\n    }\n}\n\npub(crate) fn create_tls_config_from_certs_dir(\n    certs_dir: &Path,\n    require_client_auth: bool,\n) -> anyhow::Result<rustls::ClientConfig> {\n    install_cryptographic_provider();\n\n    if !certs_dir.is_dir() {\n        anyhow::bail!(\n            \"Certificate path is not a directory: {}\",\n            certs_dir.display()\n        );\n    }\n\n    let mut all_certs: Vec<(std::path::PathBuf, Vec<CertificateDer<'static>>)> = Vec::new();\n    let mut client_key = None;\n    let mut root_store = rustls::RootCertStore::empty();\n    root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());\n\n    // Sort entries for deterministic cert/key selection across platforms\n    let mut entries: Vec<_> = std::fs::read_dir(certs_dir)?.collect::<Result<Vec<_>, _>>()?;\n    entries.sort_by_key(std::fs::DirEntry::path);\n\n    for entry in entries {\n        let path = entry.path();\n\n        if client_key.is_none()","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/network/src/tls.rs#L79-L115","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Point the config at the directory that contains the PEM certs/keys, not a file inside it.","Verify the path exists and is a directory at startup (certs_dir.is_dir()).","Check volume mounts / file deployment so the certs directory is present in the runtime environment.","Use absolute paths for cert directories to avoid working-directory surprises."],"exampleFix":"// before\ntls_certs_dir: \"/etc/certs/ca.pem\",\n// after\ntls_certs_dir: \"/etc/certs\",  // directory containing ca.pem, client.pem, client.key","handlingStrategy":"validation","validationCode":"// Rust: validate TLS certs path at startup\nlet certs_dir = std::path::Path::new(&cfg.tls_certs_dir);\nif !certs_dir.is_dir() {\n    return Err(anyhow::anyhow!(\"tls_certs_dir must be a directory: {}\", certs_dir.display()));\n}","typeGuard":"fn is_certs_dir(p: &std::path::Path) -> bool { p.is_dir() }","tryCatchPattern":"let tls_cfg = create_tls_config_from_certs_dir(certs_dir, require_client_auth)\n    .map_err(|e| { log::error!(\"tls config failed: {e}\"); e })?;","preventionTips":["Point config at the directory, not a file within it.","Use absolute paths for cert locations.","Verify cert volumes are mounted in deployments.","Check path validity in a startup self-check."],"tags":["rust","tls","network","filesystem","config"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}