quickwit-oss/quickwit · error

TLS CA certificate path (`tls.ca_path`) is not set; it is re

Error message

TLS CA certificate path (`tls.ca_path`) is not set; it is required to verify peer certificates

What it means

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.

Source

Thrown at quickwit/quickwit-transport/src/tls.rs:239

/// its own hot-reloadable identity (`cert_path`/`key_path`) and the reload task is spawned. ALPN is
/// fixed to `h2`, which is all gRPC speaks.
pub fn make_tls_client_config(tls_config: &TlsConfig) -> anyhow::Result<Arc<ClientConfig>> {
    let roots = load_root_cert_store(&tls_config.ca_path)?;
    let builder = ClientConfig::builder().with_root_certificates(roots);
    let mut client_config = if tls_config.verify_client_cert {
        let resolver = ReloadableCertResolver::load(&tls_config.cert_path, &tls_config.key_path)?;
        spawn_cert_reload_task(resolver.clone(), *tls_config.cert_poll_interval);
        builder.with_client_cert_resolver(resolver)
    } else {
        builder.with_no_client_auth()
    };
    client_config.alpn_protocols = vec![b"h2".to_vec()];
    Ok(Arc::new(client_config))
}

/// Loads the CA certificate(s) at `ca_path` into a [`RootCertStore`].
fn load_root_cert_store(ca_path: &str) -> anyhow::Result<RootCertStore> {
    anyhow::ensure!(
        !ca_path.is_empty(),
        "TLS CA certificate path (`tls.ca_path`) is not set; it is required to verify peer \
         certificates"
    );
    let ca_certs = load_certs(ca_path)
        .with_context(|| format!("failed to load TLS CA certificate(s) from `{ca_path}`"))?;
    anyhow::ensure!(
        !ca_certs.is_empty(),
        "no CA certificate found in `{ca_path}`"
    );
    let mut roots = RootCertStore::empty();
    for ca_cert in ca_certs {
        roots.add(ca_cert)?;
    }
    Ok(roots)
}

/// Spawns a background task that reloads `resolver`'s certificate, driven by both a periodic poll

View on GitHub (pinned to a39730c5cd)

Solutions

  1. Set `tls.ca_path` to a file containing the CA certificate(s) in PEM format in the relevant TLS config block
  2. Verify the config file after templating: ca_path must be a non-empty string pointing to an existing file
  3. If TLS was not intended, disable the TLS setting instead of leaving ca_path empty

Example fix

# before
tls:
  enabled: true
  ca_path: ""
# after
tls:
  enabled: true
  ca_path: "/etc/quickwit/tls/ca.crt"
Defensive patterns

Strategy: validation

Validate before calling

# Config-side precheck before deploy
tls_ca = config.get("tls", {}).get("ca_path", "")
if not tls_ca:
    raise ValueError("tls.ca_path must be set when TLS is enabled")

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of quickwit-oss/quickwit@a39730c5cd (2026-09-08). Data as JSON: /api/errors/feecae11ee96de91. Report an issue: GitHub.