nautechsystems/nautilus_trader · error
Failed to parse PEM: {e}
Error message
Failed to parse PEM: {e} What it means
`rsa_signature` first parses `private_key_pem` with the `pem` crate to extract DER bytes. This error means the input is not syntactically valid PEM (missing BEGIN/END headers, bad base64 body, stray whitespace/characters) so parsing failed. The underlying `pem` crate error is included in the message.
Source
Thrown at crates/cryptography/src/signing.rs:50
let tag = hmac::sign(&key, data.as_bytes());
Ok(hex::encode(tag.as_ref()))
}
/// Signs `data` using RSA PKCS#1 v1.5 SHA-256 with the provided private key in PEM format.
///
/// # Errors
///
/// Returns an error if:
/// - `data` is empty.
/// - `private_key_pem` is not a valid PEM-encoded PKCS#8 RSA private key or cannot be parsed.
/// - Signature generation fails due to key or cryptographic errors.
pub fn rsa_signature(private_key_pem: &str, data: &str) -> anyhow::Result<String> {
if data.is_empty() {
anyhow::bail!("Query string cannot be empty");
}
// Remove PEM headings and decode to DER bytes using the `pem` crate
let pem = pem::parse(private_key_pem.trim())
.map_err(|e| anyhow::anyhow!("Failed to parse PEM: {e}"))?;
// Ensure this is a private key
if !pem.tag().ends_with("PRIVATE KEY") {
anyhow::bail!("PEM does not contain a private key");
}
// Construct RSA key pair from PKCS#8 DER bytes
let key_pair = KeyPair::from_pkcs8(pem.contents())
.map_err(|_| anyhow::anyhow!("Failed to decode RSA private key"))?;
// Prepare RNG and output buffer (signature length = modulus length)
let rng = lc_rand::SystemRandom::new();
let mut signature = vec![0u8; key_pair.public_modulus_len()];
key_pair
.sign(
&lc_signature::RSA_PKCS1_SHA256,View on GitHub (pinned to 18893faf8b)
Solutions
- Verify the key text starts with '-----BEGIN PRIVATE KEY-----' and ends with '-----END PRIVATE KEY-----' with real newlines
- If stored in an env var or JSON, convert escaped '\n' to actual newlines before passing
- Ensure the file being read is the PEM private key, not DER or a certificate
- Read the pem crate error in the message to pinpoint whether it's base64 or structure
Example fix
// before
let sig = rsa_signature(&env_key, query)?; // env_key contains "-----BEGIN\n..." escaped
// after
let pem_text = env_key.replace("\\n", "\n");
let sig = rsa_signature(&pem_text, query)?; Defensive patterns
Strategy: validation
Validate before calling
fn looks_like_pem(s: &str) -> bool {
s.trim_start().starts_with("-----BEGIN") && s.contains("-----END")
}
if !looks_like_pem(&key) {
// fix loading/escaping before calling rsa_signature
} Type guard
fn is_private_key_pem(s: &str) -> bool {
let t = s.trim();
t.starts_with("-----BEGIN PRIVATE KEY-----")
|| t.starts_with("-----BEGIN RSA PRIVATE KEY-----")
} Try / catch
match rsa_signature(&pem, query) {
Ok(sig) => use(sig),
Err(e) if e.to_string().starts_with("Failed to parse PEM") => {
tracing::error!("bad key material: {e}"); // check newlines/escaping
}
Err(e) => return Err(e),
} Prevention
- Store keys as files, not env vars, to avoid newline mangling
- Unescape '\\n' when keys pass through JSON/env layers
- Validate PEM headers before invoking crypto
When it happens
Trigger: Calling `rsa_signature`/`py_rsa_signature` with a string that is not valid PEM: truncated key, JSON-escaped '\n' instead of real newlines, base64 corruption, or a completely different format (raw DER, SSH format, PGP block).
Common situations: Reading the key from an env var where newlines were flattened to spaces or '\n' literals; copying the key from a web console and losing formatting; passing a .der file's contents instead of PEM; wrong file read (public cert instead of key).
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- PEM does not contain a private key
- config_path must be in format 'module.path:ClassName', was {
- Query string cannot be empty
- Failed to decode RSA private key
- No valid private key found in {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/d110771297b17091.
Report an issue: GitHub.