{"record":{"id":"feecae11ee96de91","repo":"quickwit-oss/quickwit","slug":"tls-ca-certificate-path-tls-ca-path-is-not-set","errorCode":null,"errorMessage":"TLS CA certificate path (`tls.ca_path`) is not set; it is required to verify peer certificates","messagePattern":"TLS CA certificate path \\(`tls\\.ca_path`\\) is not set; it is required to verify peer certificates","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-transport/src/tls.rs","lineNumber":239,"sourceCode":"/// its own hot-reloadable identity (`cert_path`/`key_path`) and the reload task is spawned. ALPN is\n/// fixed to `h2`, which is all gRPC speaks.\npub fn make_tls_client_config(tls_config: &TlsConfig) -> anyhow::Result<Arc<ClientConfig>> {\n    let roots = load_root_cert_store(&tls_config.ca_path)?;\n    let builder = ClientConfig::builder().with_root_certificates(roots);\n    let mut client_config = if tls_config.verify_client_cert {\n        let resolver = ReloadableCertResolver::load(&tls_config.cert_path, &tls_config.key_path)?;\n        spawn_cert_reload_task(resolver.clone(), *tls_config.cert_poll_interval);\n        builder.with_client_cert_resolver(resolver)\n    } else {\n        builder.with_no_client_auth()\n    };\n    client_config.alpn_protocols = vec![b\"h2\".to_vec()];\n    Ok(Arc::new(client_config))\n}\n\n/// Loads the CA certificate(s) at `ca_path` into a [`RootCertStore`].\nfn load_root_cert_store(ca_path: &str) -> anyhow::Result<RootCertStore> {\n    anyhow::ensure!(\n        !ca_path.is_empty(),\n        \"TLS CA certificate path (`tls.ca_path`) is not set; it is required to verify peer \\\n         certificates\"\n    );\n    let ca_certs = load_certs(ca_path)\n        .with_context(|| format!(\"failed to load TLS CA certificate(s) from `{ca_path}`\"))?;\n    anyhow::ensure!(\n        !ca_certs.is_empty(),\n        \"no CA certificate found in `{ca_path}`\"\n    );\n    let mut roots = RootCertStore::empty();\n    for ca_cert in ca_certs {\n        roots.add(ca_cert)?;\n    }\n    Ok(roots)\n}\n\n/// Spawns a background task that reloads `resolver`'s certificate, driven by both a periodic poll","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-transport/src/tls.rs#L221-L257","documentation":"Quickwit's TLS transport requires a CA certificate bundle to authenticate peer certificates. load_root_cert_store rejects an empty `tls.ca_path` upfront with this ensure! error because without a CA path the resulting RootCertStore could not verify anything, for both server and client TLS configs.","triggerScenarios":"Configuring TLS enabled (e.g. grpc/peering TLS settings) while leaving `tls.ca_path` empty or unset; passing an empty string from environment/template substitution into ca_path.","commonSituations":"Deploying a Quickwit cluster with TLS turned on but forgetting to mount/distribute the CA bundle; Helm/env templating rendering an empty ca_path value; copying a TLS config from a non-TLS setup.","solutions":["Set `tls.ca_path` to a file containing the CA certificate(s) in PEM format in the relevant TLS config block","Verify the config file after templating: ca_path must be a non-empty string pointing to an existing file","If TLS was not intended, disable the TLS setting instead of leaving ca_path empty"],"exampleFix":"# before\ntls:\n  enabled: true\n  ca_path: \"\"\n# after\ntls:\n  enabled: true\n  ca_path: \"/etc/quickwit/tls/ca.crt\"","handlingStrategy":"validation","validationCode":"# Config-side precheck before deploy\ntls_ca = config.get(\"tls\", {}).get(\"ca_path\", \"\")\nif not tls_ca:\n    raise ValueError(\"tls.ca_path must be set when TLS is enabled\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always set tls.ca_path whenever any TLS option is enabled","Validate rendered config files (Helm/env templates) for empty values before rollout","Mount the CA bundle in all pods/nodes that run with TLS"],"tags":["tls","config","certificate","validation"],"backgroundTag":"missing-required-config-field","analyzedSha":"a39730c5cdcd1a4fe798403737ae293999ea21f8","analyzedAt":"2026-09-08T13:19:37.784Z","contentChangedAt":"2026-09-08T13:19:37.784Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}