nautechsystems/nautilus_trader · error

Query string cannot be empty

Error message

Query string cannot be empty

What it means

`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.

Source

Thrown at crates/cryptography/src/signing.rs:46

///
/// Returns an error if signature generation fails due to key or cryptographic errors.
pub fn hmac_signature(secret: &str, data: &str) -> anyhow::Result<String> {
    let key = hmac::Key::new(hmac::HMAC_SHA256, secret.as_bytes());
    let tag = hmac::sign(&key, data.as_bytes());
    Ok(hex::encode(tag.as_ref()))
}

/// Signs `data` using RSA PKCS#1 v1.5 SHA-256 with the provided private key in PEM format.
///
/// # Errors
///
/// Returns an error if:
/// - `data` is empty.
/// - `private_key_pem` is not a valid PEM-encoded PKCS#8 RSA private key or cannot be parsed.
/// - Signature generation fails due to key or cryptographic errors.
pub fn rsa_signature(private_key_pem: &str, data: &str) -> anyhow::Result<String> {
    if data.is_empty() {
        anyhow::bail!("Query string cannot be empty");
    }

    // Remove PEM headings and decode to DER bytes using the `pem` crate
    let pem = pem::parse(private_key_pem.trim())
        .map_err(|e| anyhow::anyhow!("Failed to parse PEM: {e}"))?;

    // Ensure this is a private key
    if !pem.tag().ends_with("PRIVATE KEY") {
        anyhow::bail!("PEM does not contain a private key");
    }

    // Construct RSA key pair from PKCS#8 DER bytes
    let key_pair = KeyPair::from_pkcs8(pem.contents())
        .map_err(|_| anyhow::anyhow!("Failed to decode RSA private key"))?;

    // Prepare RNG and output buffer (signature length = modulus length)
    let rng = lc_rand::SystemRandom::new();
    let mut signature = vec![0u8; key_pair.public_modulus_len()];

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the data/query string is non-empty before calling (build at least the required params)
  2. Guard the call site: skip signing or return early when there is nothing to sign
  3. If the target API genuinely requires signing empty payloads, add the required parameter to the query

Example fix

// before
let sig = rsa_signature(&pem, &query)?;
// after
if query.is_empty() {
    anyhow::bail!("no query parameters to sign");
}
let sig = rsa_signature(&pem, &query)?;
Defensive patterns

Strategy: validation

Validate before calling

if query.is_empty() {
    return Err(anyhow::anyhow!("query string cannot be empty"));
}
let sig = rsa_signature(&pem, query)?;

Try / catch

match rsa_signature(&pem, &query) {
    Ok(sig) => attach(sig),
    Err(e) if e.to_string().contains("cannot be empty") => {
        // skip request or rebuild params
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/4eceadb54fe033e8. Report an issue: GitHub.