zeroclaw-labs/zeroclaw · error · anyhow::Error
amqp channel '{alias}': client_cert contains no certificates
Error message
amqp channel '{alias}': client_cert contains no certificates What it means
The configured client_cert file was read successfully but contained zero PEM CERTIFICATE sections — rustls_pemfile::certs found nothing to import while building the PKCS#12 client identity in pem_to_pkcs12_der. This is a file-content problem detected before any network connection is made.
Source
Thrown at crates/zeroclaw-channels/src/amqp.rs:382
}
/// Ephemeral password protecting the in-memory PKCSidentity. The bundle is
/// built and consumed within a single connect call and never persisted, so the
/// password only has to round-trip through tcp-stream's PKCSreader.
const PKCS12_PASSWORD: &str = "zeroclaw-amqp";
/// Convert a PEM client certificate chain and private key into a PKCSDER
/// bundle suitable for tcp-stream's rustls client-auth path.
fn pem_to_pkcs12_der(cert_pem: &[u8], key_pem: &[u8], alias: &str) -> anyhow::Result<Vec<u8>> {
use p12_keystore::{Certificate, KeyStore, KeyStoreEntry, PrivateKeyChain};
let certs: Vec<Vec<u8>> = rustls_pemfile::certs(&mut &cert_pem[..])
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.map(|c| c.as_ref().to_vec())
.collect();
if certs.is_empty() {
anyhow::bail!("amqp channel '{alias}': client_cert contains no certificates");
}
let key = rustls_pemfile::private_key(&mut &key_pem[..])?.ok_or_else(|| {
anyhow::Error::msg(format!(
"amqp channel '{alias}': client_key contains no private key"
))
})?;
let chain: Vec<Certificate> = certs
.iter()
.map(|der| Certificate::from_der(der))
.collect::<Result<_, _>>()
.map_err(|e| {
anyhow::Error::msg(format!(
"amqp channel '{alias}': invalid client certificate: {e}"
))
})?;
View on GitHub (pinned to 88bb9c8533)
Solutions
- Inspect the file for a BEGIN CERTIFICATE block: openssl x509 -in client.pem -noout
- If cert and key were swapped, point client_cert at the certificate and client_key at the private key.
- Convert DER to PEM if needed: openssl x509 -inform der -in cert.der -out client.pem
Example fix
# before: client_cert points at the private key file (no CERTIFICATE blocks) client_cert = "/etc/zeroclaw/tls/client.key" # after client_cert = "/etc/zeroclaw/tls/client.pem" # leaf cert (+ chain) in PEM client_key = "/etc/zeroclaw/tls/client.key"
Defensive patterns
Strategy: validation
Validate before calling
fn pem_contains_certs(path: &std::path::Path) -> anyhow::Result<bool> {
let pem = std::fs::read(path)?;
let n = rustls_pemfile::certs(&mut &pem[..]).count();
Ok(n > 0)
} Type guard
fn looks_like_cert_pem(path: &std::path::Path) -> bool {
std::fs::read_to_string(path)
.map(|s| s.contains("BEGIN CERTIFICATE"))
.unwrap_or(false)
} Prevention
- Run openssl x509 -noout on every cert path during provisioning
- Name files by role (client.crt vs client.key) to avoid swaps
- Reject malformed PEMs at config load instead of failing at connect
When it happens
Trigger: The client_cert path points at a file with no CERTIFICATE blocks: the private key file (cert and key paths swapped), a CSR, an empty file, or a DER-encoded binary certificate instead of PEM.
Common situations: Cert/key path swaps in config; provisioning scripts writing the wrong artifact; CAs or tooling that deliver DER by default; truncated files from a partial secret sync.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- amqp channel '{}': client_cert is set but client_key is miss
- amqp channel '{}': client_key is set but client_cert is miss
- amqp.{}: dispatch = {:?} routes to the SOP engine but no SOP
- no certificates found in {path}
- ACP request_permission failed: {} ({})
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/8686433456658423.
Report an issue: GitHub.