risingwavelabs/risingwave · error · PsqlError

Failed to parse client certificate

Error message

Failed to parse client certificate

What it means

After successfully reading the client certificate bytes, init_client_config parses them with CertificateDer::pem_slice_iter. If any PEM block in the file is not a valid certificate (bad base64, wrong PEM label, garbage, or a non-PEM file), the iterator yields Err and this StartupError is raised. The library aborts because a usable client certificate is required for the TLS client-auth handshake.

Source

Thrown at src/utils/pgwire/src/ldap_auth.rs:148

                return Err(PsqlError::StartupError(
                    "Client certificate provided without private key".into(),
                ));
            };
            let client_cert_bytes = fs::read(cert).map_err(|e| {
                PsqlError::StartupError(
                    anyhow!(e)
                        .context("Failed to read client certificate")
                        .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(),
                    )

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Check the file starts with -----BEGIN CERTIFICATE----- and ends with -----END CERTIFICATE-----
  2. Verify you are not pointing at the private key or CSR; use the issued client certificate
  3. Convert DER to PEM if needed: openssl x509 -inform der -in cert.der -out cert.pem
  4. Re-export the certificate cleanly (openssl x509 -in cert.pem -out clean.pem) to strip stray whitespace

Example fix

// before (config points at key file)
client_cert = '/etc/rw/certs/client.key'
// after
client_cert = '/etc/rw/certs/client.crt'  // PEM-encoded certificate
Defensive patterns

Strategy: validation

Validate before calling

fn is_pem_certificate(path: &str) -> bool {
    std::fs::read_to_string(path).map(|s| {
        s.contains("-----BEGIN CERTIFICATE-----") && s.contains("-----END CERTIFICATE-----")
    }).unwrap_or(false)
}
// stronger check: openssl x509 -in cert.pem -noout >/dev/null

Type guard

fn looks_like_cert_pem(bytes: &[u8]) -> bool {
    let s = String::from_utf8_lossy(bytes);
    s.contains("-----BEGIN CERTIFICATE-----")
}

Try / catch

catch PsqlError::StartupError, downcast the inner anyhow chain to the rustls pki error and advise re-exporting the cert as PEM

Prevention

When it happens

Trigger: CertificateDer::pem_slice_iter(&client_cert_bytes).collect::<Result<Vec<_>,_>>() returns Err — the file at `client_cert` contains malformed PEM or non-certificate PEM blocks

Common situations: Pointing `client_cert` at the private key file or at a combined bundle that includes a key/cSR; certificate file truncated during copy/paste; file contains DER (binary) instead of PEM; extra text before/after the BEGIN/END CERTIFICATE block.

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.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/0b9b63d95119560e. Report an issue: GitHub.