{"record":{"id":"3617c6f6cd17a063","repo":"nautechsystems/nautilus_trader","slug":"failed-to-encode-params-e","errorCode":null,"errorMessage":"Failed to encode params: {e}","messagePattern":"Failed to encode params: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/kraken/src/common/credential.rs","lineNumber":181,"sourceCode":"    ///\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\n        Ok((STANDARD.encode(signature.as_ref()), post_data))\n    }\n\n    /// Sign a JSON request for Kraken Spot API (used for CancelOrderBatch, AddOrderBatch).\n    ///\n    /// These endpoints use JSON body instead of form-encoded.\n    /// Signature: HMAC-SHA512(path + SHA256(nonce + json_body))","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/kraken/src/common/credential.rs#L163-L199","documentation":"sign_spot builds the URL-encoded POST parameter string (nonce plus request params) with serde_urlencoded. This error wraps serialization failure of the params map — e.g. a key/value pair that cannot be percent-encoded into the request body.","triggerScenarios":"Calling sign_spot with params containing characters/values serde_urlencoded cannot serialize (non-UTF-8 strings, control characters introduced via env/config) — serde_urlencoded::to_string returns an Err which is wrapped.","commonSituations":"Non-UTF-8 or control characters in order parameters read from config/environment; params built from user input with invalid encoding; extremely unusual symbol strings.","solutions":["Sanitize params to valid UTF-8 strings and strip control characters before signing","Log/inspect the params map in the wrapped {e} to find the offending entry","Ensure values come from validated sources (ASCII symbol names, plain numerics)","Upgrade serde_urlencoded if a serialization bug is suspected"],"exampleFix":"// before\nparams.insert(\"pair\", weird_user_input.to_string());\nlet sig = creds.sign_spot(path, nonce, &params)?;\n// after\nlet pair: String = weird_user_input.chars().filter(|c| c.is_ascii_graphic()).collect();\nparams.insert(\"pair\", pair);\nlet sig = creds.sign_spot(path, nonce, &params)?;","handlingStrategy":"validation","validationCode":"for (k, v) in &params {\n    assert!(k.chars().all(|c| c.is_ascii_graphic() || c == ' '));\n    assert!(v.chars().all(|c| c.is_ascii_graphic() || c == ' '));\n}","typeGuard":null,"tryCatchPattern":"match creds.sign_spot(path, nonce, &params) {\n    Ok(s) => s,\n    Err(e) if e.to_string().contains(\"encode params\") => {\n        eprintln!(\"unencodable param in request: {e}\");\n        return Err(SignError::BadParams);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Keep request params ASCII/UTF-8-clean; sanitize values derived from user input","Validate symbol and parameter strings before building the signed request","Log the params map (without secrets) when signing fails to locate the bad value"],"tags":["rust","kraken","urlencoding","signing","params"],"backgroundTag":"invalid-argument-format","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}