aaif-goose/goose · error
Failed to parse EC key: {}
Error message
Failed to parse EC key: {} What it means
Thrown by goose's native-tls client-identity loader. When TlsConfig carries a client cert/key pair and goose is built with the native-tls feature, convert_key_to_pkcs8_pem normalizes the private key to PKCS#8 because reqwest's native-tls Identity::from_pkcs8_pem only accepts that format. This error means the PEM tag was 'EC PRIVATE KEY' (SEC1) but sec1::EcPrivateKey::from_der could not decode the DER body, so the key bytes are malformed, truncated, or not actually an EC key.
Source
Thrown at crates/goose-providers/src/api_client.rs:178
#[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!(
"EC key missing curve parameters. Convert to PKCS#8: \
openssl pkey -in key.pem -out key-pkcs8.pem"
)
})?;
let algorithm = pkcs8::AlgorithmIdentifierRef {
oid: sec1::ALGORITHM_OID,
parameters: Some((&curve_oid).into()),
};
let info = pkcs8::PrivateKeyInfo::new(algorithm, 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)))View on GitHub (pinned to 3810898a74)
Solutions
- Regenerate or re-export the key cleanly: openssl ec -in key.pem -check -out checked.pem; if -check fails the file itself is corrupt
- Convert the key to PKCS#8, which every code path accepts: openssl pkey -in key.pem -out key-pkcs8.pem, then point TlsConfig at key-pkcs8.pem
- Confirm the file really is an unencrypted EC private key: openssl pkey -in key.pem -noout -text
- If the key came through a secret store, re-download the original bytes instead of copying text from a terminal/browser
Example fix
# before (broken key bytes under SEC1 header) -----BEGIN EC PRIVATE KEY----- MHQCAQEE...(truncated/corrupted base64) -----END EC PRIVATE KEY----- # after: convert to PKCS#8 and use that path openssl pkey -in key.pem -out key-pkcs8.pem # TlsConfig::new().with_client_cert_and_key(cert.pem.into(), "key-pkcs8.pem".into())
Defensive patterns
Strategy: validation
Validate before calling
fn check_ec_key_parses(path: &str) -> anyhow::Result<()> {
let pem_text = std::fs::read_to_string(path)?;
let parsed = pem::parse(&pem_text)
.map_err(|e| anyhow::anyhow!("bad PEM: {e}"))?;
if parsed.tag() != "EC PRIVATE KEY" { return Ok(()); }
sec1::EcPrivateKey::from_der(parsed.contents())
.map_err(|e| anyhow::anyhow!("SEC1 body will fail: {e}"))?;
Ok(())
} Try / catch
// At client-construction time, convert once and report the file, not the DER internals:
let tls = match TlsConfig::new().with_client_cert_and_key(cert, key) { /* build later */ };
match client_result {
Err(e) if e.to_string().contains("Failed to parse EC key") =>
eprintln!("{key_path} is not a valid SEC1 EC key; run: openssl pkey -in {key_path} -out {key_path}.pkcs8"),
other => other?,
} Prevention
- Generate mTLS keys directly as PKCS#8: openssl genpkey (not genrsa/ecparam) or openssl pkey -out key-pkcs8.pem
- Never hand-copy PEM bodies between terminals or chat tools; transfer files or use secret managers byte-for-byte
- Add a CI step that runs openssl pkey -in <key> -noout on every cert/key pair before deployment
When it happens
Trigger: Calling TlsConfig::with_client_cert_and_key(cert, key) with native-tls enabled where the key file's PEM header is '-----BEGIN EC PRIVATE KEY-----' and the DER payload fails SEC1 parsing: base64 corrupted by copy/paste, file truncated, wrong file (e.g. a CSR) renamed to .pem, or hand-edited key text.
Common situations: Legacy SEC1 keys generated with 'openssl ecparam -genkey' and then mangled by copy/paste (lost lines, joined base64), secrets from Kubernetes/corporate PKI exported through JSON wrappers that strip trailing '=' padding, or a passphrase-encrypted body under a plain EC header.
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
- EC key missing curve parameters. Convert to PKCS#8: openssl
- Unsupported key format '{}'. Expected PKCS#8, PKCS#1, or SEC
- Failed to create identity from cert and key: {}
- Failed to parse PEM key: {}
- Failed to encode PKCS#8: {}
AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16).
Data as JSON: /api/errors/4a5df1eefece83dd.
Report an issue: GitHub.