EpicGames/lore · error · anyhow::Error

Maintenance TLS is partially configured: cert_file and…

Error message

Maintenance TLS is partially configured: cert_file and pkey_file must both be set or both be absent

What it means

lore-server refuses to start the maintenance gRPC server when TLS is half-configured: only one of cert_file and pkey_file was provided. A certificate without its private key (or vice versa) cannot form a usable TLS identity, so the code rejects it explicitly instead of failing later at bind time.

Solutions

  1. Set both cert_file and pkey_file in the maintenance TLS config, or remove both to run without TLS
  2. Check the config file/template for a missing or commented-out line
  3. Verify secret/file mounts or env vars delivering both paths are present at startup

Example fix

// before
tls = { cert_file = "/etc/certs/server.crt" }
// after
tls = { cert_file = "/etc/certs/server.crt", pkey_file = "/etc/certs/server.key" }
Defensive patterns

Strategy: validation

Validate before calling

fn validate_maintenance_tls(tls: &MaintenanceTlsConfig) -> Result<(), String> {
    match (tls.cert_file.as_deref(), tls.pkey_file.as_deref()) {
        (Some(_), Some(_)) | (None, None) => Ok(()),
        _ => Err("cert_file and pkey_file must both be set or both absent".into()),
    }
}

Type guard

fn tls_config_complete(tls: &MaintenanceTlsConfig) -> bool {
    tls.cert_file.is_some() == tls.pkey_file.is_some()
}

Try / catch

match server::launch(cfg) {
    Err(e) if e.to_string().contains("partially configured") => eprintln!("fix TLS config: set both cert_file and pkey_file"),
    Err(e) => return Err(e),
    Ok(s) => s,
}

Prevention

When it happens

Trigger: Setting maintenance_tls.cert_file without maintenance_tls.pkey_file (or the reverse) in the server config before calling the gRPC server setup; the match arm (Some,None)/(None,Some) in the maintenance TLS setup returns this anyhow error.

Common situations: Hand-edited TOML/YAML config where one line was commented out; secret-management systems injecting only one of the two paths; templated configs where one mount failed; partial migration from plaintext to TLS.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of EpicGames/lore@074eb0b0d1 (2026-09-13). Data as JSON: /api/errors/4e4dd51abf360493. Report an issue: GitHub.

Appendix: source

Thrown at lore-server/src/grpc/server.rs:952

    addr: SocketAddr,
    cert_path: Option<PathBuf>,
    key_path: Option<PathBuf>,
    cert_chain_path: Option<PathBuf>,
    signal: impl Future<Output = ()> + Send + 'static,
) -> Result<()> {
    let environment_svc = LoreEnvironmentService::maintenance(environment.clone());
    let environment_v1_svc = LoreEnvironmentV1Service::maintenance(environment);

    let mut server = Server::builder();
    match (cert_path, key_path) {
        (Some(cert_path), Some(key_path)) => {
            info!("Loading maintenance TLS certs - cert: {cert_path:?} key: {key_path:?}");
            let tls_config = build_server_tls_config(cert_path, key_path, cert_chain_path)?;
            server = server.tls_config(tls_config)?;
        }
        (None, None) => {}
        _ => {
            return Err(anyhow!(
                "Maintenance TLS is partially configured: cert_file and pkey_file must both be set or both be absent"
            ));
        }
    }

    // Served from net like the other listeners. No `CoreHopLayer`: both handlers
    // only return UNAVAILABLE, so there is nothing to keep off net.
    let router = server
        .add_service(EnvironmentServiceServer::new(environment_svc))
        .add_service(environment_v1_server::EnvironmentServiceServer::new(
            environment_v1_svc,
        ));
    lore_spawn_net!(async move { router.serve_with_shutdown(addr, signal).await }).await??;

    Ok(())
}

#[cfg(test)]

View on GitHub (pinned to 074eb0b0d1)