{"record":{"id":"f2565815d53b45ae","repo":"risingwavelabs/risingwave","slug":"failed-to-read-client-key","errorCode":null,"errorMessage":"Failed to read client key","messagePattern":"Failed to read client key","errorType":"exception","errorClass":"PsqlError","httpStatus":null,"severity":"error","filePath":"src/utils/pgwire/src/ldap_auth.rs","lineNumber":142,"sourceCode":"            }\n        }\n        let tls_client_config = tls_client_config.with_root_certificates(root_cert_store);\n\n        if let Some(cert) = &self.cert {\n            let Some(key) = &self.key else {\n                return Err(PsqlError::StartupError(\n                    \"Client certificate provided without private key\".into(),\n                ));\n            };\n            let client_cert_bytes = fs::read(cert).map_err(|e| {\n                PsqlError::StartupError(\n                    anyhow!(e)\n                        .context(\"Failed to read client certificate\")\n                        .into(),\n                )\n            })?;\n            let client_key_bytes = fs::read(key).map_err(|e| {\n                PsqlError::StartupError(anyhow!(e).context(\"Failed to read client key\").into())\n            })?;\n            let client_certs = CertificateDer::pem_slice_iter(&client_cert_bytes)\n                .collect::<Result<Vec<_>, _>>()\n                .map_err(|e| {\n                    PsqlError::StartupError(\n                        anyhow!(e)\n                            .context(\"Failed to parse client certificate\")\n                            .into(),\n                    )\n                })?;\n\n            let client_private_key =\n                PrivateKeyDer::from_pem_slice(&client_key_bytes).map_err(|e| {\n                    PsqlError::StartupError(anyhow!(e).context(\"Failed to parse client key\").into())\n                })?;\n\n            tls_client_config\n                .with_client_auth_cert(client_certs, client_private_key)","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/risingwavelabs/risingwave/blob/6469eb736d691e8e9b8a419a57edd6429ca77417/src/utils/pgwire/src/ldap_auth.rs#L124-L160","documentation":"During LDAP-over-TLS client certificate setup, RisingWave reads the mTLS client key file configured via `client_key` and wraps any fs::read failure in a PsqlError::StartupError. This means the key file path was supplied but could not be read: the file does not exist, the path is wrong, or the process lacks read permission. Startup of the LDAP connection is aborted because client authentication cannot proceed without the key.","triggerScenarios":"init_client_config is called from establish_connection when the LDAP config has client cert/key options set; fs::read(key) fails with io::Error (NotFound, PermissionDenied, is-a-directory, etc.)","commonSituations":"Typo in the `client_key` path in the CREATE CONNECTION / LDAP config; relative path resolved against a different working directory of the RisingWave process; key file deleted or renamed after config was written; file mounted with restrictive permissions the rw user cannot read.","solutions":["Verify the `client_key` path exists and is readable by the RisingWave process user (ls -l, check permissions)","Use an absolute path in the config instead of a relative one","Confirm the file is the private key matching the configured client certificate, not the certificate itself","If client auth is not required, remove the client cert/key options so the no-client-auth branch is used"],"exampleFix":"// before\nclient_key = 'certs/client.key'   // relative path, wrong cwd\n// after\nclient_key = '/etc/risingwave/certs/client.key'  // absolute, chmod 400 readable by rw user","handlingStrategy":"validation","validationCode":"// before configuring LDAP mTLS\nlet key = std::path::Path::new(client_key_path);\nif !key.is_file() {\n    return Err(format!(\"client_key not a readable file: {}\", client_key_path));\n}\nmatch std::fs::File::open(key) {\n    Ok(_) => {},\n    Err(e) => return Err(format!(\"client_key unreadable: {e}\")),\n}","typeGuard":"fn is_readable_file(p: &str) -> bool {\n    std::path::Path::new(p).is_file() && std::fs::File::open(p).is_ok()\n}","tryCatchPattern":"match PsqlError::StartupError chain, inspect downcast_ref::<std::io::Error>() for NotFound/PermissionDenied and emit a targeted config hint","preventionTips":["Always use absolute paths for cert/key files in deployment configs","Set key file perms to 0400 owned by the RisingWave user","Smoke-test file readability in the deployment/init script before starting the server","Mount certs via secrets manager with explicit path checks in readiness probes"],"tags":["ldap","tls","mtls","file-io","startup"],"backgroundTag":"file-read-failed","analyzedSha":"6469eb736d691e8e9b8a419a57edd6429ca77417","analyzedAt":"2026-09-11T21:06:21.487Z","contentChangedAt":"2026-09-11T21:06:21.487Z","schemaVersion":2},"datasetVersion":"2026-09-14T21:17:11.552Z"}