{"record":{"id":"08fd5e3a941d67bc","repo":"aaif-goose/goose","slug":"failed-to-read-ca-certificate","errorCode":null,"errorMessage":"Failed to read CA certificate: {}","messagePattern":"Failed to read CA certificate: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/goose-providers/src/api_client.rs","lineNumber":123,"sourceCode":"            let identity = {\n                let pkcs8_key_pem = convert_key_to_pkcs8_pem(&key_pem)?;\n                Identity::from_pkcs8_pem(cert_pem.as_bytes(), pkcs8_key_pem.as_bytes()).map_err(\n                    |e| anyhow::anyhow!(\"Failed to create identity from cert and key: {}\", e),\n                )?\n            };\n\n            Ok(Some(identity))\n        } else {\n            Ok(None)\n        }\n    }\n\n    #[cfg(any(feature = \"rustls-tls\", feature = \"native-tls\"))]\n    fn load_ca_certificates(&self) -> Result<Vec<Certificate>> {\n        match &self.ca_cert_path {\n            Some(ca_path) => {\n                let ca_pem = read_to_string(ca_path)\n                    .map_err(|e| anyhow::anyhow!(\"Failed to read CA certificate: {}\", e))?;\n\n                let certs = Certificate::from_pem_bundle(ca_pem.as_bytes())\n                    .map_err(|e| anyhow::anyhow!(\"Failed to parse CA certificate bundle: {}\", e))?;\n\n                Ok(certs)\n            }\n            None => Ok(Vec::new()),\n        }\n    }\n}\n\nimpl Default for TlsConfig {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\n/// Convert a PEM private key from any format (PKCS#1, SEC1, PKCS#8) to PKCS#8 PEM.","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/aaif-goose/goose/blob/3810898a7447ec3299be72e223d3570a7aabf0ab/crates/goose-providers/src/api_client.rs#L105-L141","documentation":"When a provider config sets a custom CA bundle (ca_cert_path), TlsConfig::load_ca_certificates reads it with std::fs::read_to_string before parsing. Any I/O failure on that file — nonexistent path, permission denied, directory, non-UTF8 content — is reported as 'Failed to read CA certificate: {io_error}' at client build time.","triggerScenarios":"Configuring ca_cert_path for a self-signed or corporate CA where the path is wrong, relative to a different working directory, unreadable by the goose process, or the file is binary DER instead of PEM text.","commonSituations":"Corporate proxy CA shipped as .der/.crt binary; container mounts the bundle at a different path than configured; path typo; file readable only by root while goose runs unprivileged.","solutions":["Confirm the file exists and is readable: ls -l <ca_cert_path>","Convert DER CA to PEM if needed: openssl x509 -inform der -in ca.der -out ca.pem","Use an absolute path in the provider config","Fix ownership/mode so the goose user can read it"],"exampleFix":"# before\nca_cert_path: ./corp-ca.crt   # relative path, DER binary\n\n# after (shell + config)\n$ openssl x509 -inform der -in corp-ca.crt -out /etc/goose/tls/corp-ca.pem\n# config: ca_cert_path: /etc/goose/tls/corp-ca.pem","handlingStrategy":"validation","validationCode":"let ca = std::path::Path::new(&ca_cert_path);\nanyhow::ensure!(\n    std::fs::read_to_string(ca).is_ok(),\n    \"CA bundle unreadable or non-text: {ca_cert_path}\"\n);","typeGuard":"fn ca_bundle_is_readable(path: &std::path::Path) -> bool {\n    std::fs::metadata(path).map(|m| m.is_file()).unwrap_or(false)\n        && std::fs::read_to_string(path).is_ok()\n}","tryCatchPattern":null,"preventionTips":["Use absolute paths for ca_cert_path","Convert DER CAs to PEM: openssl x509 -inform der -in ca.crt -out ca.pem","Make the bundle readable by the goose runtime user (0644 is fine for public CA material)","Verify container mounts expose the file at the configured path"],"tags":["rust","tls","ca-certificate","filesystem","config"],"backgroundTag":null,"analyzedSha":"3810898a7447ec3299be72e223d3570a7aabf0ab","analyzedAt":"2026-08-16T10:14:26.282Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}