risingwavelabs/risingwave · error · PsqlError
Failed to parse client key
Error message
Failed to parse client key
What it means
The client key bytes were read fine but PrivateKeyDer::from_pem_slice failed to decode them as a PEM-encoded private key (RSA, PKCS8, EC, etc.). This StartupError indicates the key material is not parseable by rustls, so the client-auth TLS config cannot be built and connection establishment stops.
Source
Thrown at src/utils/pgwire/src/ldap_auth.rs:156
.into(),
)
})?;
let client_key_bytes = fs::read(key).map_err(|e| {
PsqlError::StartupError(anyhow!(e).context("Failed to read client key").into())
})?;
let client_certs = CertificateDer::pem_slice_iter(&client_cert_bytes)
.collect::<Result<Vec<_>, _>>()
.map_err(|e| {
PsqlError::StartupError(
anyhow!(e)
.context("Failed to parse client certificate")
.into(),
)
})?;
let client_private_key =
PrivateKeyDer::from_pem_slice(&client_key_bytes).map_err(|e| {
PsqlError::StartupError(anyhow!(e).context("Failed to parse client key").into())
})?;
tls_client_config
.with_client_auth_cert(client_certs, client_private_key)
.map_err(|err| {
PsqlError::StartupError(
anyhow!(err)
.context("Failed to set client certificate")
.into(),
)
})
} else {
Ok(tls_client_config.with_no_client_auth())
}
}
}
/// LDAP configuration extracted from HBA entryView on GitHub (pinned to 6469eb736d)
Solutions
- Convert the key to unencrypted PKCS#8: openssl pkcs8 -topk8 -nocrypt -in key.pem -out key.pkcs8.pem
- Decrypt an encrypted key first: openssl rsa -in encrypted.key -out decrypted.key
- Confirm the file holds the private key (-----BEGIN PRIVATE KEY-----/BEGIN RSA PRIVATE KEY-----), not the cert
- Ensure the key corresponds to the configured client certificate
Example fix
// before openssl req -newkey rsa:2048 -nodes # produced 'BEGIN PRIVATE KEY' with local params // after (normalize for rustls) openssl pkcs8 -topk8 -nocrypt -in client.key -out client_pkcs8.key # then: client_key = '/etc/rw/certs/client_pkcs8.key'
Defensive patterns
Strategy: validation
Validate before calling
fn is_parseable_key_pem(path: &str) -> bool {
std::fs::read_to_string(path).map(|s| {
["-----BEGIN PRIVATE KEY-----", "-----BEGIN RSA PRIVATE KEY-----", "-----BEGIN EC PRIVATE KEY-----"]
.iter().any(|h| s.starts_with(h)) && !s.contains("ENCRYPTED")
}).unwrap_or(false)
} Type guard
fn is_unencrypted_key_pem(s: &str) -> bool {
s.contains("-----BEGIN") && s.contains("PRIVATE KEY-----") && !s.contains("ENCRYPTED")
} Try / catch
catch PsqlError::StartupError, inspect the rustls pki-types error in the anyhow chain and re-raise with an 'openssl pkcs8 -topk8 -nocrypt' hint
Prevention
- Normalize all keys to unencrypted PKCS#8 before deployment
- Never deploy passphrase-protected keys (automated processes cannot decrypt them)
- Run `openssl pkey -in key.pem -noout` as a deploy-time check
- Generate cert+key pairs in one step to guarantee they match
When it happens
Trigger: PrivateKeyDer::from_pem_slice(&client_key_bytes) returns Err — key file is malformed PEM, uses an unsupported key format, or is encrypted (password-protected) PEM
Common situations: Key is in a format rustls rejects (e.g. traditional 'BEGIN RSA PRIVATE KEY' with exotic parameters, or PKCS#12 .pfx); key is passphrase-encrypted; file actually contains a certificate or the CSR; key truncated or CRLF-mangled on Windows.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to parse client certificate
- No private key found
- Failed to parse CA certificate
- Failed to read client key
- Failed to set client certificate
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/70f55a2054186fe9.
Report an issue: GitHub.