{"record":{"id":"4eceadb54fe033e8","repo":"nautechsystems/nautilus_trader","slug":"query-string-cannot-be-empty","errorCode":null,"errorMessage":"Query string cannot be empty","messagePattern":"Query string cannot be empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/cryptography/src/signing.rs","lineNumber":46,"sourceCode":"///\n/// Returns an error if signature generation fails due to key or cryptographic errors.\npub fn hmac_signature(secret: &str, data: &str) -> anyhow::Result<String> {\n    let key = hmac::Key::new(hmac::HMAC_SHA256, secret.as_bytes());\n    let tag = hmac::sign(&key, data.as_bytes());\n    Ok(hex::encode(tag.as_ref()))\n}\n\n/// Signs `data` using RSA PKCS#1 v1.5 SHA-256 with the provided private key in PEM format.\n///\n/// # Errors\n///\n/// Returns an error if:\n/// - `data` is empty.\n/// - `private_key_pem` is not a valid PEM-encoded PKCS#8 RSA private key or cannot be parsed.\n/// - Signature generation fails due to key or cryptographic errors.\npub fn rsa_signature(private_key_pem: &str, data: &str) -> anyhow::Result<String> {\n    if data.is_empty() {\n        anyhow::bail!(\"Query string cannot be empty\");\n    }\n\n    // Remove PEM headings and decode to DER bytes using the `pem` crate\n    let pem = pem::parse(private_key_pem.trim())\n        .map_err(|e| anyhow::anyhow!(\"Failed to parse PEM: {e}\"))?;\n\n    // Ensure this is a private key\n    if !pem.tag().ends_with(\"PRIVATE KEY\") {\n        anyhow::bail!(\"PEM does not contain a private key\");\n    }\n\n    // Construct RSA key pair from PKCS#8 DER bytes\n    let key_pair = KeyPair::from_pkcs8(pem.contents())\n        .map_err(|_| anyhow::anyhow!(\"Failed to decode RSA private key\"))?;\n\n    // Prepare RNG and output buffer (signature length = modulus length)\n    let rng = lc_rand::SystemRandom::new();\n    let mut signature = vec![0u8; key_pair.public_modulus_len()];","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/cryptography/src/signing.rs#L28-L64","documentation":"`rsa_signature` signs a request payload (typically a query string) with an RSA private key, and rejects empty input up front because signing empty data is almost always a caller bug and produces a useless/invalid request signature. The error is raised before any crypto work happens.","triggerScenarios":"Calling `rsa_signature(private_key_pem, \"\")` or `py_rsa_signature` with an empty string as `data` — e.g. a request with no query parameters, or a variable that failed to be populated upstream.","commonSituations":"Signing exchange API requests where the query string was built from zero parameters; a bug where params were dropped before signing; empty URL construction from missing configuration.","solutions":["Ensure the data/query string is non-empty before calling (build at least the required params)","Guard the call site: skip signing or return early when there is nothing to sign","If the target API genuinely requires signing empty payloads, add the required parameter to the query"],"exampleFix":"// before\nlet sig = rsa_signature(&pem, &query)?;\n// after\nif query.is_empty() {\n    anyhow::bail!(\"no query parameters to sign\");\n}\nlet sig = rsa_signature(&pem, &query)?;","handlingStrategy":"validation","validationCode":"if query.is_empty() {\n    return Err(anyhow::anyhow!(\"query string cannot be empty\"));\n}\nlet sig = rsa_signature(&pem, query)?;","typeGuard":null,"tryCatchPattern":"match rsa_signature(&pem, &query) {\n    Ok(sig) => attach(sig),\n    Err(e) if e.to_string().contains(\"cannot be empty\") => {\n        // skip request or rebuild params\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Build query strings with at least the API's required parameters","Assert non-empty payload at the request-builder boundary","Trace where empty params originate (missing config, dropped fields)"],"tags":["rust","cryptography","rsa","validation"],"backgroundTag":"empty-required-field","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"}