{"record":{"id":"27b73cf79f25c84d","repo":"dani-garcia/vaultwarden","slug":"can-t-convert-to-number","errorCode":null,"errorMessage":"Can't convert to number","messagePattern":"Can't convert to number","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/util.rs","lineNumber":707,"sourceCode":"        match self {\n            NumberOrString::Number(n) => n.to_string(),\n            NumberOrString::String(s) => s,\n        }\n    }\n\n    #[expect(clippy::wrong_self_convention)]\n    pub fn into_i32(&self) -> Result<i32, crate::Error> {\n        use std::num::ParseIntError as PIE;\n        match self {\n            NumberOrString::Number(n) => {\n                if let Some(n) = n.to_i32() {\n                    Ok(n)\n                } else {\n                    err!(\"Number does not fit in i32\")\n                }\n            }\n            NumberOrString::String(s) => {\n                s.parse().map_err(|e: PIE| crate::Error::new(\"Can't convert to number\", e.to_string()))\n            }\n        }\n    }\n\n    #[expect(clippy::wrong_self_convention)]\n    pub fn into_i64(&self) -> Result<i64, crate::Error> {\n        use std::num::ParseIntError as PIE;\n        match self {\n            NumberOrString::Number(n) => Ok(*n),\n            NumberOrString::String(s) => {\n                s.parse().map_err(|e: PIE| crate::Error::new(\"Can't convert to number\", e.to_string()))\n            }\n        }\n    }\n}\n\n//\n// Retry methods","sourceCodeStart":689,"sourceCodeEnd":725,"githubUrl":"https://github.com/dani-garcia/vaultwarden/blob/cc67d644f62605cb46f4d16c4a2eed1a861cc8bb/src/util.rs#L689-L725","documentation":"NumberOrString::into_i32 parses a String variant into i32. When the string is not a valid integer (ParseIntError), it is mapped to a crate Error with message 'Can't convert to number' and the parse error as detail.","triggerScenarios":"Calling into_i32() on a NumberOrString::String holding non-numeric or out-of-i32-range text, e.g. client-supplied JSON fields like folder ids' iteration counts or API request numbers sent as strings.","commonSituations":"Bitwarden clients sending numeric fields as strings with formatting (spaces, signs, underscores); empty string ''; values exceeding i32 range (use into_i64 for those, though that yields a distinct 'does not fit' error path).","solutions":["Ensure the string is a plain base-10 integer before calling into_i32","Validate/sanitize client input earlier in the API handler and return a 400 instead","Use into_i64() if values may exceed the i32 range","Log the error detail (e.to_string()) which contains the exact parse failure"],"exampleFix":"// before\nlet n = value.into_i32()?; // \"12abc\" -> Can't convert to number\n// after\nlet n = value.into_i64()?; // or validate: s.trim().parse::<i64>().is_ok()","handlingStrategy":"try-catch","validationCode":"function isI32String(s) { const n = Number(s); return Number.isInteger(n) && n >= -2147483648 && n <= 2147483647; }","typeGuard":"function asI32(v) { return typeof v === 'string' && /^-?\\d+$/.test(v.trim()) && Number(v) <= 2147483647 ? Number(v) : null; }","tryCatchPattern":"match value.into_i32() {\n  Ok(n) => n,\n  Err(e) => { warn!(\"Non-numeric field: {}\", e); return Err(e); }\n}","preventionTips":["Validate numeric string fields at the API boundary before domain logic","Reject decimals, whitespace, and empty strings explicitly","Use into_i64 for fields that may exceed i32 range","Include the parse-error detail in client-facing 400 responses"],"tags":["parsing","type-conversion","api-input"],"backgroundTag":"invalid-argument-format","analyzedSha":"cc67d644f62605cb46f4d16c4a2eed1a861cc8bb","analyzedAt":"2026-09-19T08:28:46.370Z","contentChangedAt":"2026-09-19T08:28:46.370Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}