zeroclaw-labs/zeroclaw · error
no certificates found in {path}
Error message
no certificates found in {path} What it means
`load_certs` opens the file and parses PEM certificates with rustls-pemfile; separate contexts cover 'cannot open' and 'failed to parse'. This error is the third, distinct case: the file opened and parsed without error but contained zero `BEGIN CERTIFICATE` blocks. Typical causes are a private-key-only PEM, an empty file, or a non-PEM file passed where a certificate bundle was expected.
Source
Thrown at crates/zeroclaw-gateway/src/tls.rs:172
) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
self.inner.verify_tls13_signature(message, cert, dss)
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
self.inner.supported_verify_schemes()
}
}
/// Load PEM-encoded certificates from a file.
fn load_certs(path: &str) -> Result<Vec<CertificateDer<'static>>> {
let file = std::fs::File::open(path)
.with_context(|| format!("cannot open certificate file: {path}"))?;
let mut reader = std::io::BufReader::new(file);
let certs: Vec<CertificateDer<'static>> = rustls_pemfile::certs(&mut reader)
.collect::<std::result::Result<Vec<_>, _>>()
.with_context(|| format!("failed to parse PEM certificates from {path}"))?;
if certs.is_empty() {
anyhow::bail!("no certificates found in {path}");
}
Ok(certs)
}
/// Load a PEM-encoded private key from a file.
fn load_private_key(path: &str) -> Result<PrivateKeyDer<'static>> {
let file = std::fs::File::open(path)
.with_context(|| format!("cannot open private key file: {path}"))?;
let mut reader = std::io::BufReader::new(file);
let key = rustls_pemfile::private_key(&mut reader)
.with_context(|| format!("failed to parse private key from {path}"))?
.ok_or_else(|| {
::zeroclaw_log::record!(
ERROR,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
.with_outcome(::zeroclaw_log::EventOutcome::Failure)
.with_attrs(::serde_json::json!({"path": path})),
"TLS private key file contains no key"View on GitHub (pinned to 88bb9c8533)
Solutions
- Inspect the file for `-----BEGIN CERTIFICATE-----` blocks; if absent, it is not a cert PEM
- Check for swapped cert/key paths in the TLS configuration
- Convert DER to PEM if needed: `openssl x509 -in cert.der -inform DER -out cert.pem`
Example fix
# before cert_path = "config/server.key" # key file passed as cert cert_path = "config/server.key" # after cert_path = "config/server.crt" # PEM containing BEGIN CERTIFICATE blocks key_path = "config/server.key"
Defensive patterns
Strategy: validation
Validate before calling
fn pem_has_certificates(path: &std::path::Path) -> bool {
std::fs::read_to_string(path)
.map(|s| s.contains("-----BEGIN CERTIFICATE-----"))
.unwrap_or(false)
}
assert!(pem_has_certificates(cert_path), "cert file contains no certificates"); Prevention
- Use unambiguous file naming (server.crt vs server.key) so cert and key paths cannot be swapped
- Verify certificates with `openssl x509 -in file -noout` before configuring TLS
- Keep full-chain PEM (leaf + intermediates); keys-only PEMs trigger exactly this error
When it happens
Trigger: Passing the key file where the certificate is expected (swapped cert/key config values); an empty or truncated certificate file; a DER-encoded file that is not PEM.
Common situations: Swapping TLS cert and key paths in config; a certificate file overwritten with key material; downloading the chain in DER instead of PEM from the CA.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- amqp channel '{alias}': client_cert contains no certificates
- amqp channel '{}': client_cert is set but client_key is miss
- amqp channel '{}': client_key is set but client_cert is miss
- Refusing to transmit sensitive data over non-HTTPS URL: URL
- QQ gateway request failed ({status}): {err}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/af029ba93f176d2b.
Report an issue: GitHub.