{"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":"http","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"src/util.rs","lineNumber":710,"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":692,"sourceCodeEnd":728,"githubUrl":"https://github.com/dani-garcia/vaultwarden/blob/0cefa4cca7c9f2a5579dd290f78193b543818c51/src/util.rs#L692-L728","documentation":"NumberOrString is the type for request/config fields that arrive either as JSON numbers or numeric strings. into_i32() parses the string form with str::parse::<i32>(); a non-numeric string or a value outside i32 range produces this crate::Error carrying the underlying ParseIntError text. Callers such as two-factor activation (data.r#type.into_i32()?) and WebAuthn operations (data.id.into_i32()?) turn it into a failed request.","triggerScenarios":"POST /two-factor/... with \"type\": \"abc\" or \"1x\"; enabling/managing WebAuthn credentials with \"id\": \"12x\" or an out-of-range value like \"99999999999\"; any client sending these integer fields as unparsable strings or exceeding i32 range (-2147483648..2147483647).","commonSituations":"Third-party clients or scripts hand-crafting JSON with quoted numbers; payloads copied from docs containing placeholders; client libraries that stringify every value.","solutions":["Send the field as a plain JSON number, or a clean numeric string like \"1\"","Strip spaces, unit suffixes, thousand separators, and decimals from the value","If the value legitimately exceeds i32, it belongs in an i64-backed field/endpoint (e.g. file sizes)"],"exampleFix":"// before\n{ \"type\": \"authenticator\" }\n// after\n{ \"type\": 0 }","handlingStrategy":"validation","validationCode":"// Client-side pre-submit check\nfunction assertI32(value, field) {\n  const n = typeof value === 'number' ? value : Number.parseInt(String(value), 10);\n  if (!Number.isInteger(n) || n < -2147483648 || n > 2147483647) {\n    throw new Error(`${field} must be an integer within i32 range, got: ${JSON.stringify(value)}`);\n  }\n}","typeGuard":"const isI32Like = (v) =>\n  typeof v === 'number'\n    ? Number.isInteger(v) && v >= -2147483648 && v <= 2147483647\n    : /^-?\\d+$/.test(String(v).trim()) && Number(v) >= -2147483648 && Number(v) <= 2147483647;","tryCatchPattern":null,"preventionTips":["Use JSON numbers for numeric fields, not strings","Validate payloads with a schema (zod / JSON Schema) before sending","Do not copy example payloads containing placeholder strings"],"tags":["rust","json","parsing","api","two-factor"],"backgroundTag":null,"analyzedSha":"0cefa4cca7c9f2a5579dd290f78193b543818c51","analyzedAt":"2026-08-16T07:44:56.102Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}