nautechsystems/nautilus_trader · error · anyhow::Error
Failed to decode API secret: {e}
Error message
Failed to decode API secret: {e} What it means
Kraken API secrets are base64-encoded; sign_spot decodes the stored api_secret with STANDARD base64 before computing the HMAC signature. This error wraps the base64 decode failure when the configured API secret is not valid base64.
Source
Thrown at crates/adapters/kraken/src/common/credential.rs:174
}
/// Sign a request for Kraken Spot REST API.
///
/// Kraken Spot uses HMAC-SHA512 with the following message:
/// - path + SHA256(nonce + POST data)
/// - The secret is base64 decoded before signing
///
/// Note: "nonce + POST data" means the nonce value string is prepended
/// to the URL-encoded POST body, e.g., "1234567890nonce=1234567890¶m=value".
pub fn sign_spot(
&self,
path: &str,
nonce: u64,
params: &HashMap<String, String>,
) -> anyhow::Result<(String, String)> {
let secret = STANDARD
.decode(&self.api_secret)
.map_err(|e| anyhow::anyhow!("Failed to decode API secret: {e}"))?;
let nonce_str = nonce.to_string();
let mut post_data = format!("nonce={nonce_str}");
if !params.is_empty() {
let encoded = serde_urlencoded::to_string(params)
.map_err(|e| anyhow::anyhow!("Failed to encode params: {e}"))?;
post_data.push('&');
post_data.push_str(&encoded);
}
let sha_input = format!("{nonce_str}{post_data}");
let hash = digest::digest(&digest::SHA256, sha_input.as_bytes());
let mut message = path.as_bytes().to_vec();
message.extend_from_slice(hash.as_ref());
let key = hmac::Key::new(hmac::HMAC_SHA512, &secret);
let signature = hmac::sign(&key, &message);
View on GitHub (pinned to 18893faf8b)
Solutions
- Paste the API secret exactly as provided by Kraken (it is base64 already) without decoding or modifying it
- Strip whitespace/newlines from the secret string before constructing credentials
- Verify you are not swapping the API key and secret fields
- Validate with: python -c "import base64; base64.b64decode('<secret>')"
Example fix
// before let creds = KrakenCredentials::new(api_key, my_decoded_hex_secret); // after let secret = my_raw_kraken_secret.trim().to_string(); let creds = KrakenCredentials::new(api_key, secret);
Defensive patterns
Strategy: validation
Validate before calling
import base64 base64.b64decode(secret, validate=True) # raises if not valid standard base64
Type guard
function isValidBase64(s) { return /^[A-Za-z0-9+/]+={0,2}$/.test(s) && Buffer.from(s, 'base64').length > 0; } Try / catch
match creds.sign_spot(path, nonce, ¶ms) {
Ok(sig) => sig,
Err(e) if e.to_string().contains("decode API secret") => {
eprintln!("api_secret is not valid base64; re-copy from Kraken: {e}");
return Err(SignError::BadSecret);
}
Err(e) => return Err(e),
} Prevention
- Store the Kraken secret exactly as issued (base64), never pre-decoded
- Trim whitespace/newlines when loading secrets from env or files
- Validate the secret with a base64 decode at credential construction time
When it happens
Trigger: Creating/using KrakenCredentials with an api_secret that is the raw (already-decoded) hex/plain secret, contains whitespace, uses a URL-safe alphabet, or is otherwise invalid standard base64.
Common situations: Copying the API secret from Kraken and accidentally decoding it first; trailing newline/space from a copy-paste or env var; misconfiguring secret vs key fields; reading the secret from a file with encoding issues.
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 params: {e}
- failed to mint Lighter auth token: {e}
- Kraken Spot does not support the demo environment
- Redis config error: username supplied without password. Eith
- Binance Spot market data mode SBE requires Ed25519 API crede
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/fe2233a4c20524c6.
Report an issue: GitHub.