nautechsystems/nautilus_trader · error
Invalid Ed25519 private key length
Error message
Invalid Ed25519 private key length
What it means
`ed25519_signature` requires the private key seed to be exactly 32 bytes (Ed25519 seed size). The seed slice is converted with `try_into()` to a `[u8; 32]`; if the length differs, this error is returned before any signing occurs.
Source
Thrown at crates/cryptography/src/signing.rs:87
&rng,
data.as_bytes(),
&mut signature,
)
.map_err(|_| anyhow::anyhow!("Failed to generate RSA signature"))?;
Ok(BASE64_STANDARD.encode(signature))
}
/// Signs `data` using Ed25519 with the provided private key seed.
///
/// # Errors
///
/// Returns an error if the provided private key seed is invalid or signature creation fails.
pub fn ed25519_signature(private_key: &[u8], data: &str) -> anyhow::Result<String> {
let signing_key = SigningKey::from_bytes(
private_key
.try_into()
.map_err(|_| anyhow::anyhow!("Invalid Ed25519 private key length"))?,
);
let signature: Ed25519Signature = signing_key.sign(data.as_bytes());
Ok(BASE64_STANDARD.encode(signature.to_bytes()))
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
#[rstest]
#[case(
"mysecretkey",
"data-to-sign",
"19ed21a8b2a6b847d7d7aea059ab3134cd58f13c860cfbe89338c718685fe077"
)]
#[case(View on GitHub (pinned to 18893faf8b)
Solutions
- Decode the key material first: hex -> `bytes.fromhex(key)` or base64 -> `base64.b64decode(key)` so the result is exactly 32 bytes.
- If you have a 64-byte Ed25519 secret key, pass only the first 32 bytes: `secret[:32]`.
- Check the length in the caller before invoking: `len(key) == 32`.
- If using Python, ensure the argument is `bytes`, not `str`.
Example fix
// before let key = hex_string.as_bytes(); // 64 bytes of ASCII hex -> error let sig = ed25519_signature(&key, data)?; // after let key = hex::decode(hex_string)?; // 32 raw bytes assert_eq!(key.len(), 32); let sig = ed25519_signature(&key, data)?;
Defensive patterns
Strategy: validation
Validate before calling
// Python caller: decode and length-check the seed before calling the binding
import base64, binascii
def ed25519_seed(raw: str | bytes) -> bytes:
if isinstance(raw, str):
key = bytes.fromhex(raw) if all(c in "0123456789abcdefABCDEF" for c in raw) else base64.b64decode(raw)
else:
key = raw
if len(key) != 32:
raise ValueError(f"Ed25519 seed must be 32 bytes, got {len(key)}")
return key[:32] Type guard
// Rust caller
defn is_valid_seed(key: &[u8]) -> bool { key.len() == 32 }
// Python caller
def is_valid_seed(key) -> bool:
return isinstance(key, (bytes, bytearray)) and len(key) == 32 Try / catch
try:
sig = py_ed25519_signature(seed, data)
except (ValueError, RuntimeError) as e:
if "Invalid Ed25519 private key length" in str(e):
raise ValueError(f"seed must be 32 decoded bytes, got {len(seed)}") from e
raise Prevention
- Always store/pass Ed25519 seeds as raw 32-byte arrays, never as hex/base64 strings without decoding at the boundary.
- Slice 64-byte expanded secret keys down to `key[:32]` before use.
- Assert `len(key) == 32` in unit tests for any code path that constructs keys.
- In Python, enforce `bytes` typing on key parameters; never let `str` through.
When it happens
Trigger: Calling `ed25519_signature(private_key, data)` (or the Python binding `py_ed25519_signature`) with a seed that is not exactly 32 bytes: hex/base64 string passed raw instead of decoded, a 64-byte public key or full 64-byte secret key passed instead of the 32-byte seed, or an empty/truncated byte array.
Common situations: Passing a hex-encoded string (64 chars = 64 bytes raw) without decoding; passing the 64-byte expanded secret from a keypair file; Python callers passing a `str` whose UTF-8 bytes are used directly instead of `bytes.fromhex`/`base64.b64decode`; off-by-one slicing when splitting a key file.
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.
Related errors
- Failed to generate RSA signature
- Query string cannot be empty
- Failed to parse PEM: {e}
- PEM does not contain a private key
- Failed to decode RSA private key
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/3f78dd0cac198921.
Report an issue: GitHub.