{"record":{"id":"8b305c2a0bd52567","repo":"xai-org/x-algorithm","slug":"env-grpc-mtls-client-ca-path-must-be-set","errorCode":null,"errorMessage":"{ENV_GRPC_MTLS_CLIENT_CA_PATH} must be set","messagePattern":"(.+?) must be set","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"visibility-filtering/config.rs","lineNumber":115,"sourceCode":"\n        let server_key_path = std::env::var(ENV_GRPC_MTLS_SERVER_KEY_PATH)\n            .ok()\n            .filter(|v| !v.is_empty())\n            .ok_or_else(|| anyhow::anyhow!(\"{ENV_GRPC_MTLS_SERVER_KEY_PATH} must be set\"))?;\n\n        let server_crt_path = std::env::var(ENV_GRPC_MTLS_SERVER_CRT_PATH)\n            .ok()\n            .filter(|v| !v.is_empty())\n            .ok_or_else(|| anyhow::anyhow!(\"{ENV_GRPC_MTLS_SERVER_CRT_PATH} must be set\"))?;\n\n        let server_chain_path = std::env::var(ENV_GRPC_MTLS_SERVER_CHAIN_PATH)\n            .ok()\n            .filter(|v| !v.is_empty());\n\n        let client_ca_path = std::env::var(ENV_GRPC_MTLS_CLIENT_CA_PATH)\n            .ok()\n            .filter(|v| !v.is_empty())\n            .ok_or_else(|| anyhow::anyhow!(\"{ENV_GRPC_MTLS_CLIENT_CA_PATH} must be set\"))?;\n\n        Ok(Some(Self {\n            server_key_path,\n            server_crt_path,\n            server_chain_path,\n            client_ca_path,\n        }))\n    }\n\n    pub fn server_tls_config(&self) -> anyhow::Result<tonic::transport::ServerTlsConfig> {\n        let mut cert_pem = std::fs::read(&self.server_crt_path)?;\n        let key_pem = std::fs::read(&self.server_key_path)?;\n        let client_ca_pem = std::fs::read(&self.client_ca_path)?;\n\n        if let Some(chain_path) = self.server_chain_path.as_ref() {\n            let chain_pem = std::fs::read(chain_path)?;\n            if !cert_pem.ends_with(b\"\\n\") {\n                cert_pem.push(b'\\n');","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/xai-org/x-algorithm/blob/24c60942c5c5fdad3a6addffb4c6e6d2f228f04f/visibility-filtering/config.rs#L97-L133","documentation":"Raised in from_env when mTLS is enabled and the server key/cert are set, but GRPC_MTLS_CLIENT_CA_PATH (the CA that validates client certificates) is unset or empty. Mutual TLS requires the client CA so the server can verify incoming client certs.","triggerScenarios":"Enabling mTLS with server key and cert configured but no client CA path; unlike SERVER_CHAIN_PATH (optional), CLIENT_CA_PATH is mandatory.","commonSituations":"Configuring only the server side of mTLS and forgetting client verification; CA file not mounted as a secret; env name typos; staging configs diverging from production.","solutions":["Set GRPC_MTLS_CLIENT_CA_PATH to the CA certificate bundle used to verify clients","Confirm the CA file exists and is readable inside the container","If client-certificate verification is not desired, disable mTLS rather than omitting the CA","Validate the whole mTLS env set (key, crt, client CA) in a pre-deployment check"],"exampleFix":"# before\nexport GRPC_MTLS_SERVER_KEY_PATH=/etc/certs/server.key\nexport GRPC_MTLS_SERVER_CRT_PATH=/etc/certs/server.crt\n# client CA missing -> error\n\n# after\nexport GRPC_MTLS_SERVER_KEY_PATH=/etc/certs/server.key\nexport GRPC_MTLS_SERVER_CRT_PATH=/etc/certs/server.crt\nexport GRPC_MTLS_CLIENT_CA_PATH=/etc/certs/client_ca.crt","handlingStrategy":"validation","validationCode":"assert!(!env::var(\"GRPC_MTLS_CLIENT_CA_PATH\").unwrap_or_default().trim().is_empty());","typeGuard":null,"tryCatchPattern":"null","preventionTips":["Mount the client CA as a secret and reference it in env","Distinguish optional (SERVER_CHAIN) vs required (CLIENT_CA) vars in docs","Add integration test for from_env covering all env combinations"],"tags":["mtls","env-var","tls","client-ca","rust"],"backgroundTag":"missing-env-var","analyzedSha":"24c60942c5c5fdad3a6addffb4c6e6d2f228f04f","analyzedAt":"2026-08-28T11:40:14.686Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}