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