{"record":{"id":"e85403f682060107","repo":"astrid-runtime/astrid","slug":"empty-byte-specifier","errorCode":null,"errorMessage":"empty byte specifier","messagePattern":"empty byte specifier","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/quota.rs","lineNumber":338,"sourceCode":"    let body = client\n        .request(AdminRequestKind::QuotaSet {\n            principal: target.clone(),\n            quotas,\n        })\n        .await?;\n    let _ = into_result(body)?;\n    println!(\"Updated quotas for '{target}'.\");\n    Ok(ExitCode::SUCCESS)\n}\n\n// ── byte/duration parsers ──────────────────────────────────────────\n\n/// Parse `\"32\"`, `\"32B\"`, `\"32KB\"`, `\"32MB\"`, `\"32GB\"`, `\"32KiB\"`,\n/// `\"32MiB\"`, `\"32GiB\"`, `\"32TB\"`, `\"32TiB\"`. Lowercase accepted.\npub(crate) fn parse_bytes(s: &str) -> Result<u64> {\n    let trimmed = s.trim();\n    if trimmed.is_empty() {\n        anyhow::bail!(\"empty byte specifier\");\n    }\n    // Strip optional `/s` (used by --ipc-rate).\n    let body = trimmed.strip_suffix(\"/s\").unwrap_or(trimmed);\n    let (num_part, mult) = parse_numeric_suffix(body)?;\n    let num: f64 = num_part\n        .parse()\n        .with_context(|| format!(\"not a number: {num_part}\"))?;\n    if num.is_sign_negative() || !num.is_finite() {\n        anyhow::bail!(\"byte value must be non-negative and finite\");\n    }\n    #[expect(\n        clippy::cast_sign_loss,\n        clippy::cast_precision_loss,\n        clippy::cast_possible_truncation,\n        reason = \"guarded by sign and finite checks above\"\n    )]\n    let bytes = (num * (mult as f64)) as u64;\n    Ok(bytes)","sourceCodeStart":320,"sourceCodeEnd":356,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/quota.rs#L320-L356","documentation":"Input validation at the top of parse_bytes: the byte-size specifier, after trimming, is an empty string. The parser requires a numeric value plus optional suffix (e.g. 32, 32KB, 32MiB); an empty CLI value for a quota set operation has no meaning and is rejected before suffix parsing. A generic validation bail.","triggerScenarios":"Passing an empty or whitespace-only value to `--storage`, `--ipc-rate`, or another byte-parsed quota flag.","commonSituations":"An unset environment variable expanded to empty in a shell script (`--storage \"$QUOTA\"` with QUOTA=\"\"), a config file with a blank field, or accidentally passing `--storage \"\"`.","solutions":["Supply a non-empty value with a unit, e.g. `--storage 10GB`","Check shell variables/config files for empty values before invoking the command","Quote and default variables: `${QUOTA:-10GB}`"],"exampleFix":"// before\nastrid quota set --storage \"\"\n// after\nastrid quota set --storage 10GB","handlingStrategy":"validation","validationCode":"fn ensure_non_empty(s: &str) -> Result<&str, String> {\n    let t = s.trim();\n    if t.is_empty() { Err(\"byte specifier must not be empty\".into()) } else { Ok(t) }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use shell parameter defaults (${VAR:-10GB}) for quota inputs","Validate config-file fields are non-empty before passing to CLI","Never interpolate possibly-empty env vars directly into flags"],"tags":["cli","parsing","validation"],"backgroundTag":"empty-required-field","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}