aaif-goose/goose · error
Failed to parse PEM key: {}
Error message
Failed to parse PEM key: {} What it means
On the native-tls build, convert_key_to_pkcs8_pem re-encodes legacy key formats (PKCS#1 RSA, SEC1 EC) into PKCS#8. First it runs pem::parse on the key text; if the file is not a valid PEM document at all — binary DER, encrypted/garbled content, missing armor — parsing fails with 'Failed to parse PEM key: {err}'. This is a key-format problem, not a file-read problem.
Source
Thrown at crates/goose-providers/src/api_client.rs:165
/// Generated by default with `openssl genrsa`. Very common in older setups, tutorials,
/// and CA-issued key files.
/// - **SEC1** (`-----BEGIN EC PRIVATE KEY-----`): Legacy EC-specific format.
/// Generated by default with `openssl ecparam -genkey`. Common with EC certificates.
/// - **PKCS#8** (`-----BEGIN PRIVATE KEY-----`): Generic, algorithm-agnostic wrapper.
/// This is the only format native-tls accepts.
///
/// Without this conversion, users with legacy-format keys (Kubernetes secrets, corporate
/// PKI, etc.) would get a cryptic "Failed to create identity" error and need to manually
/// run `openssl pkey -in key.pem -out key-pkcs8.pem`.
///
/// Note: the rustls code path (`Identity::from_pem`) accepts all formats natively,
/// so this conversion is only needed for native-tls.
#[cfg(feature = "native-tls")]
fn convert_key_to_pkcs8_pem(key_pem_str: &str) -> Result<String> {
use pkcs8::der::{Decode, Encode};
let parsed =
pem::parse(key_pem_str).map_err(|e| anyhow::anyhow!("Failed to parse PEM key: {}", e))?;
match parsed.tag() {
"PRIVATE KEY" => Ok(key_pem_str.to_string()),
"RSA PRIVATE KEY" => {
let info = pkcs8::PrivateKeyInfo::new(pkcs1::ALGORITHM_ID, parsed.contents());
let der_bytes = info
.to_der()
.map_err(|e| anyhow::anyhow!("Failed to encode PKCS#8: {}", e))?;
Ok(pem::encode(&pem::Pem::new("PRIVATE KEY", der_bytes)))
}
"EC PRIVATE KEY" => {
let ec_key = sec1::EcPrivateKey::from_der(parsed.contents())
.map_err(|e| anyhow::anyhow!("Failed to parse EC key: {}", e))?;
let curve_oid = ec_key
.parameters
.and_then(|p| p.named_curve())
.ok_or_else(|| {
anyhow::anyhow!(View on GitHub (pinned to 3810898a74)
Solutions
- Check the first line of the key file: it must read -----BEGIN ... PRIVATE KEY-----
- Convert DER to PEM: openssl pkey -inform der -in key.der -out key.pem
- Extract a key from PKCS#12 instead: openssl pkcs12 -in bundle.pfx -nocerts -nodes -out key.pem
- Remove passphrase protection: openssl pkey -in key.enc -passin pass:... -out key.pem
Example fix
# before client_identity: key_path: /etc/goose/tls/client-key.der # binary DER # after (shell) $ openssl pkey -inform der -in /etc/goose/tls/client-key.der -out /etc/goose/tls/client-key.pem # key_path: /etc/goose/tls/client-key.pem (text PEM with armor)
Defensive patterns
Strategy: validation
Validate before calling
let key_text = std::fs::read_to_string(&key_path)?;
anyhow::ensure!(
key_text.contains("-----BEGIN") && key_text.contains("PRIVATE KEY-----"),
"key file is not PEM armor; convert DER: openssl pkey -inform der -in {key_path} -out key.pem"
); Type guard
fn looks_like_pem_key(text: &str) -> bool {
text.contains("-----BEGIN") && text.contains("PRIVATE KEY-----")
} Prevention
- Never point key_path at .pfx/.p12 or DER files — convert first with openssl
- Extract keys from PKCS#12 with openssl pkcs12 -nocerts -nodes
- After any transfer, verify: openssl pkey -in key.pem -noout must succeed
- Keep armored PEM files out of editors that re-wrap long lines
When it happens
Trigger: client_identity key_path contains DER-encoded key bytes (no -----BEGIN----- armor), a PKCS#12 (.p12/.pfx) file, an encrypted PEM, or a corrupted download. Only compiled with the native-tls feature; fires at client build time after the key file was read as text.
Common situations: Kubernetes secrets or corporate PKI handing out DER keys; users pointing key_path at a .pfx bundle; Windows-exported keys in PKCS#12; truncated file transfers.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to encode PKCS#8: {}
- Failed to create identity from cert and key: {}
- Failed to read client private key: {}
- Failed to parse CA certificate bundle: {}
- Failed to parse EC key: {}
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/0fd17b686bab905f.
Report an issue: GitHub.