risingwavelabs/risingwave · error
No private key found
Error message
No private key found
What it means
Thrown by `load_private_key` when building TLS credentials. The function iterates PEM sections with `PrivatePkcs8KeyDer::pem_slice_iter` looking for a PKCS#8 private key block, and this error is raised when no such block is present in the supplied string or file. Only PKCS#8 (`-----BEGIN PRIVATE KEY-----`) format is accepted by this parser.
Source
Thrown at src/connector/src/connector_common/common.rs:1305
CertificateDer::pem_slice_iter(&cert_bytes)
.collect::<Result<Vec<_>, _>>()
.context("failed to parse certificates")
.map_err(Into::into)
}
pub(crate) fn load_private_key(
certificate: &str,
) -> ConnectorResult<rustls_pki_types::PrivateKeyDer<'static>> {
let cert_bytes = if let Some(path) = certificate.strip_prefix("fs://") {
std::fs::read_to_string(path).map(|cert| cert.as_bytes().to_owned())?
} else {
certificate.as_bytes().to_owned()
};
let cert = PrivatePkcs8KeyDer::pem_slice_iter(&cert_bytes)
.next()
.ok_or_else(|| anyhow!("No private key found"))?
.context("failed to parse the private key")?;
Ok(cert.into())
}
#[serde_as]
#[derive(Deserialize, Debug, Clone, WithOptions)]
pub struct MongodbCommon {
/// The URL of `MongoDB`
#[serde(rename = "mongodb.url")]
pub connect_uri: String,
/// The collection name where data should be written to or read from. For sinks, the format is
/// `db_name.collection_name`. Data can also be written to dynamic collections, see `collection.name.field`
/// for more information.
#[serde(rename = "collection.name")]
pub collection_name: String,
}
impl EnforceSecret for MongodbCommon {View on GitHub (pinned to 6469eb736d)
Solutions
- Convert the key to PKCS#8 format, e.g. `openssl pkcs8 -topk8 -nocrypt -in key.pem -out key_pkcs8.pem`, and use that.
- Check that the private-key option points to (or inline contains) the actual key file, not the certificate.
- Verify the file contents contain a `-----BEGIN PRIVATE KEY-----` block (`grep 'PRIVATE KEY' key.pem`).
- If using a file, ensure the `fs://` prefix is present; if inlining, ensure the PEM text is fully included.
Example fix
// before key = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpA..."; // after key = "-----BEGIN PRIVATE KEY-----\nMIIEvQ..."; // converted with openssl pkcs8 -topk8 -nocrypt
Defensive patterns
Strategy: validation
Validate before calling
fn has_pkcs8_pem(s: &str) -> bool { s.contains("-----BEGIN PRIVATE KEY-----") }
if !has_pkcs8_pem(key_input) { return Err("private key must be PKCS#8 PEM (BEGIN PRIVATE KEY)"); } Type guard
fn is_pkcs8_key_pem(s: &str) -> bool {
s.contains("-----BEGIN PRIVATE KEY-----") && !s.contains("RSA PRIVATE KEY") && !s.contains("EC PRIVATE KEY")
} Try / catch
let key = load_private_key(&cfg.tls_key).map_err(|e| match e.to_string().as_str() {
"No private key found" => ConfigError::new("key file has no PKCS#8 PEM block; convert with `openssl pkcs8 -topk8 -nocrypt`"),
_ => e.into(),
})?; Prevention
- Always store keys in PKCS#8 format (`BEGIN PRIVATE KEY` header)
- Double-check key vs certificate paths in TLS options
- Keep key files non-empty and readable; validate contents before startup
When it happens
Trigger: Passing a value to the private-key option (TLS config) whose bytes contain no `-----BEGIN PRIVATE KEY-----` PEM section — e.g. an empty string, a file containing only the certificate, or a key in PKCS#1 (`BEGIN RSA PRIVATE KEY`) or SECG1 (`BEGIN EC PRIVATE KEY`) legacy formats.
Common situations: Providing the certificate path where the key path is expected; a key file with legacy `RSA PRIVATE KEY` headers instead of PKCS#8; missing `fs://` prefix so an inline value is parsed as a path (or vice versa); empty/unreadable key file contents after `fs://` read.
Related errors
- Failed to parse client key
- parse ca file error: {0}
- Failed to parse CA certificate
- Failed to add CA certificate
- Failed to add native CA certificate
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/4583371f897ae982.
Report an issue: GitHub.