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

  1. Confirm the file exists and is readable: ls -l <ca_cert_path>
  2. Convert DER CA to PEM if needed: openssl x509 -inform der -in ca.der -out ca.pem
  3. Use an absolute path in the provider config
  4. 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

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

Related errors


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/08fd5e3a941d67bc. Report an issue: GitHub.