{"record":{"id":"bb0801aa3bb07e62","repo":"pydantic/monty","slug":"name-must-be-an-integer","errorCode":null,"errorMessage":"{name} must be an integer","messagePattern":"(.+?) must be an integer","errorType":"validation","errorClass":"napi::Error","httpStatus":null,"severity":"error","filePath":"crates/monty-js/src/limits.rs","lineNumber":109,"sourceCode":"\n/// Converts a JavaScript `number` used for a size/count limit into `u64`.\n///\n/// JavaScript numbers are IEEE-754 doubles, so integers above `2^53 - 1`\n/// cannot be represented exactly. Rejecting values outside the safe integer\n/// range avoids silently rounding resource limits at the napi boundary.\n///\n/// Returns `Err` for non-finite, negative, fractional, or out-of-range inputs.\n/// This helper does not panic.\npub(crate) fn js_number_to_u64(value: f64, name: &str) -> Result<u64> {\n    const JS_MAX_SAFE_INTEGER: u64 = (1_u64 << 53) - 1;\n\n    match value {\n        v if !v.is_finite() => Err(Error::new(\n            Status::InvalidArg,\n            format!(\"{name} must be a finite number\"),\n        )),\n        v if v < 0.0 => Err(Error::new(Status::InvalidArg, format!(\"{name} must be non-negative\"))),\n        v if v.fract() != 0.0 => Err(Error::new(Status::InvalidArg, format!(\"{name} must be an integer\"))),\n        v if v > JS_MAX_SAFE_INTEGER as f64 => Err(Error::new(\n            Status::InvalidArg,\n            format!(\"{name} must be a safe integer (<= {JS_MAX_SAFE_INTEGER})\"),\n        )),\n        v => {\n            #[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)]\n            let value = v as u64;\n            Ok(value)\n        }\n    }\n}\n","sourceCodeStart":91,"sourceCodeEnd":121,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty-js/src/limits.rs#L91-L121","documentation":"napi-rs `Error` (Status::InvalidArg) thrown by `js_number_to_u64` when a numeric option has a fractional part. The target `u64`/`usize` limit must be a whole number, so values like 1.5 are rejected even if positive and in range.","triggerScenarios":"Passing a non-integer such as `requestTimeout: 1.5`, `maxMemory: 1048576.25`, or a value computed via division (`total / workers`) into a limit option routed through `js_number_to_usize`.","commonSituations":"Dividing memory or worker counts across processes without flooring; percentages converted to bytes; numbers read from config files with decimal units.","solutions":["Use `Math.floor`/`Math.round`/`Math.trunc` to integerize the value before passing it","Compute sizes with integer arithmetic where possible","Guard with `Number.isInteger(value)` before the call"],"exampleFix":"// before\nconst maxMemory = totalMemory / workers; // e.g. 1048576.5\n// after\nconst maxMemory = Math.floor(totalMemory / workers);","handlingStrategy":"validation","validationCode":"function assertInteger(name, v) {\n  if (!Number.isInteger(v)) throw new TypeError(`${name} must be an integer`);\n  return v;\n}","typeGuard":"const isNonNegativeInt = (v) => Number.isInteger(v) && v >= 0;","tryCatchPattern":"try {\n  await Monty.create({ maxMemory: rawBytes });\n} catch (e) {\n  if (/must be an integer/.test(e?.message ?? '')) {\n    await Monty.create({ maxMemory: Math.floor(rawBytes) });\n  } else throw e;\n}","preventionTips":["Use Math.floor/round on division-derived sizes","Prefer integer byte arithmetic over fractional unit conversion","Add Number.isInteger asserts at config-load time"],"tags":["javascript","napi","argument-validation","numbers"],"backgroundTag":"invalid-argument-value","analyzedSha":"adc986b362e3961f407868cb118a99fe831b9e61","analyzedAt":"2026-09-13T19:19:18.698Z","contentChangedAt":"2026-09-13T19:19:18.698Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}