aaif-goose/goose · error
Failed to read CA certificate: {}
Error message
Failed to read CA certificate: {} What it means
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.
Source
Thrown at crates/goose-providers/src/api_client.rs:123
let identity = {
let pkcs8_key_pem = convert_key_to_pkcs8_pem(&key_pem)?;
Identity::from_pkcs8_pem(cert_pem.as_bytes(), pkcs8_key_pem.as_bytes()).map_err(
|e| anyhow::anyhow!("Failed to create identity from cert and key: {}", e),
)?
};
Ok(Some(identity))
} else {
Ok(None)
}
}
#[cfg(any(feature = "rustls-tls", feature = "native-tls"))]
fn load_ca_certificates(&self) -> Result<Vec<Certificate>> {
match &self.ca_cert_path {
Some(ca_path) => {
let ca_pem = read_to_string(ca_path)
.map_err(|e| anyhow::anyhow!("Failed to read CA certificate: {}", e))?;
let certs = Certificate::from_pem_bundle(ca_pem.as_bytes())
.map_err(|e| anyhow::anyhow!("Failed to parse CA certificate bundle: {}", e))?;
Ok(certs)
}
None => Ok(Vec::new()),
}
}
}
impl Default for TlsConfig {
fn default() -> Self {
Self::new()
}
}
/// Convert a PEM private key from any format (PKCS#1, SEC1, PKCS#8) to PKCS#8 PEM.View on GitHub (pinned to 3810898a74)
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
Example fix
# before ca_cert_path: ./corp-ca.crt # relative path, DER binary # after (shell + config) $ openssl x509 -inform der -in corp-ca.crt -out /etc/goose/tls/corp-ca.pem # config: ca_cert_path: /etc/goose/tls/corp-ca.pem
Defensive patterns
Strategy: validation
Validate before calling
let ca = std::path::Path::new(&ca_cert_path);
anyhow::ensure!(
std::fs::read_to_string(ca).is_ok(),
"CA bundle unreadable or non-text: {ca_cert_path}"
); Type guard
fn ca_bundle_is_readable(path: &std::path::Path) -> bool {
std::fs::metadata(path).map(|m| m.is_file()).unwrap_or(false)
&& std::fs::read_to_string(path).is_ok()
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Failed to read client certificate: {}
- Failed to read client private key: {}
- Failed to parse CA certificate bundle: {}
- Failed to create directory: {}
- Provider '{}' has dynamic_models: false but no static models
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/08fd5e3a941d67bc.
Report an issue: GitHub.