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
- Set both cert_file and pkey_file in the maintenance TLS config, or remove both to run without TLS
- Check the config file/template for a missing or commented-out line
- 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
- Validate TLS config completeness at config-load time
- Keep cert/key pairs defined together in templates and secret manifests
- Add a startup smoke test that loads the production config
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.
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Missing QUIC certificate config
- Failed client config
- [environment.endpoint] auth_url is set but [server.auth] is…
- [environment.endpoint] auth_url and [server.auth]…
- Immutable store must be a local store
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)