{"record":{"id":"fe2233a4c20524c6","repo":"nautechsystems/nautilus_trader","slug":"failed-to-decode-api-secret-e","errorCode":null,"errorMessage":"Failed to decode API secret: {e}","messagePattern":"Failed to decode API secret: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/kraken/src/common/credential.rs","lineNumber":174,"sourceCode":"    }\n\n    /// Sign a request for Kraken Spot REST API.\n    ///\n    /// Kraken Spot uses HMAC-SHA512 with the following message:\n    /// - path + SHA256(nonce + POST data)\n    /// - The secret is base64 decoded before signing\n    ///\n    /// Note: \"nonce + POST data\" means the nonce value string is prepended\n    /// to the URL-encoded POST body, e.g., \"1234567890nonce=1234567890&param=value\".\n    pub fn sign_spot(\n        &self,\n        path: &str,\n        nonce: u64,\n        params: &HashMap<String, String>,\n    ) -> anyhow::Result<(String, String)> {\n        let secret = STANDARD\n            .decode(&self.api_secret)\n            .map_err(|e| anyhow::anyhow!(\"Failed to decode API secret: {e}\"))?;\n\n        let nonce_str = nonce.to_string();\n        let mut post_data = format!(\"nonce={nonce_str}\");\n\n        if !params.is_empty() {\n            let encoded = serde_urlencoded::to_string(params)\n                .map_err(|e| anyhow::anyhow!(\"Failed to encode params: {e}\"))?;\n            post_data.push('&');\n            post_data.push_str(&encoded);\n        }\n\n        let sha_input = format!(\"{nonce_str}{post_data}\");\n        let hash = digest::digest(&digest::SHA256, sha_input.as_bytes());\n        let mut message = path.as_bytes().to_vec();\n        message.extend_from_slice(hash.as_ref());\n        let key = hmac::Key::new(hmac::HMAC_SHA512, &secret);\n        let signature = hmac::sign(&key, &message);\n","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/kraken/src/common/credential.rs#L156-L192","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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>')\""],"exampleFix":"// before\nlet creds = KrakenCredentials::new(api_key, my_decoded_hex_secret);\n// after\nlet secret = my_raw_kraken_secret.trim().to_string();\nlet creds = KrakenCredentials::new(api_key, secret);","handlingStrategy":"validation","validationCode":"import base64\nbase64.b64decode(secret, validate=True)  # raises if not valid standard base64","typeGuard":"function isValidBase64(s) { return /^[A-Za-z0-9+/]+={0,2}$/.test(s) && Buffer.from(s, 'base64').length > 0; }","tryCatchPattern":"match creds.sign_spot(path, nonce, &params) {\n    Ok(sig) => sig,\n    Err(e) if e.to_string().contains(\"decode API secret\") => {\n        eprintln!(\"api_secret is not valid base64; re-copy from Kraken: {e}\");\n        return Err(SignError::BadSecret);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["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"],"tags":["rust","kraken","base64","credentials","signing"],"backgroundTag":"missing-credentials","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}