{"record":{"id":"e0a80d48d0f2c04f","repo":"nautechsystems/nautilus_trader","slug":"batch-size-must-be-greater-than-zero","errorCode":null,"errorMessage":"`batch_size` must be greater than zero","messagePattern":"`batch_size` must be greater than zero","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/infrastructure/src/redis/queries.rs","lineNumber":210,"sourceCode":"\n        Ok(bytes_results)\n    }\n\n    /// Bulk reads multiple keys from Redis using MGET, batched into chunks.\n    ///\n    /// Keys are batched into chunks of `batch_size` to avoid exceeding Redis\n    /// request size limits on some providers.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if `batch_size` is zero or if the underlying Redis MGET operation fails.\n    pub async fn read_bulk_batched(\n        con: &ConnectionManager,\n        keys: &[String],\n        batch_size: usize,\n    ) -> anyhow::Result<Vec<Option<Bytes>>> {\n        if batch_size == 0 {\n            anyhow::bail!(\"`batch_size` must be greater than zero\");\n        }\n\n        if keys.is_empty() {\n            return Ok(vec![]);\n        }\n\n        let mut all_results: Vec<Option<Bytes>> = Vec::with_capacity(keys.len());\n\n        for chunk in keys.chunks(batch_size) {\n            let mut con = con.clone();\n\n            let results: Vec<Option<Vec<u8>>> =\n                redis::cmd(\"MGET\").arg(chunk).query_async(&mut con).await?;\n\n            all_results.extend(results.into_iter().map(|opt| opt.map(Bytes::from)));\n        }\n\n        Ok(all_results)","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/infrastructure/src/redis/queries.rs#L192-L228","documentation":"read_bulk_batched fetches Redis keys in batches of batch_size using MGET-style reads. A batch size of zero would loop forever or produce empty batches, so the function validates the argument up front and bails. This is a defensive parameter check on a public API.","triggerScenarios":"Calling read_bulk_batched with batch_size == 0 — e.g. a computed batch size from an empty/default config value, or a caller deriving batch size from a slice length that happens to be zero.","commonSituations":"Configuration where a batch-size setting defaults to 0 or is parsed from an empty string; math like keys.len() / divisor yielding 0; passing through a user-supplied value without validation.","solutions":["Pass a batch_size >= 1, e.g. saturate at the call site with batch_size.max(1).","Validate/normalize the batch-size config value at startup before calling this function.","Guard derived values: if batch size is computed, clamp it to a sensible minimum (e.g. 100).","Note the empty-keys early return already returns Ok(vec![]) — only pass keys when non-empty and pair with a positive batch size."],"exampleFix":"// before\nlet values = read_bulk_batched(&con, &keys, config.batch_size).await?;\n// after\nlet batch = config.batch_size.max(1);\nlet values = read_bulk_batched(&con, &keys, batch).await?;","handlingStrategy":"validation","validationCode":"// Rust: clamp batch size before calling\nlet batch_size = config.batch_size.unwrap_or(256).max(1);\nassert!(batch_size > 0, \"batch_size must be positive\");","typeGuard":"fn positive_batch_size(n: usize) -> Option<usize> {\n    if n == 0 { None } else { Some(n) }\n}","tryCatchPattern":"match read_bulk_batched(&con, &keys, batch_size).await {\n    Ok(values) => { /* use values */ }\n    Err(e) if e.to_string().contains(\"batch_size\") => {\n        // retry once with a safe default\n        read_bulk_batched(&con, &keys, 256).await\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Never allow batch-size config to default to 0; enforce a positive minimum at parse time.","Clamp computed batch sizes (e.g. .max(1)) before calling batched APIs.","Rely on the empty-keys early return instead of special-casing empty inputs yourself.","Unit-test batched readers with batch_size edge values (0 and 1)."],"tags":["redis","validation","argument-error","batching"],"backgroundTag":"invalid-argument-value","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"}