{"record":{"id":"b7221d5ce734eb20","repo":"pydantic/monty","slug":"name-must-be-non-negative","errorCode":null,"errorMessage":"{name} must be non-negative","messagePattern":"(.+?) must be non-negative","errorType":"validation","errorClass":"napi::Error","httpStatus":null,"severity":"error","filePath":"crates/monty-js/src/limits.rs","lineNumber":108,"sourceCode":"}\n\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":90,"sourceCodeEnd":121,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty-js/src/limits.rs#L90-L121","documentation":"Generic guard inside the napi limit-conversion helper `js_number_to_u64`, which converts a JavaScript `number` limit into a Rust `u64`. It fires when the caller passes a negative number for a size/count limit option (e.g. a pool or session limit configured from TypeScript). Negative limits are meaningless, so the helper returns a napi Err naming the option (`{name} must be non-negative`) instead of wrapping or truncating the value. This is a deliberate input-validation sentinel, not a bug: pass a non-negative (and safe-integer) number.","triggerScenarios":"Passing a negative number to any numeric limit option that routes through `js_number_to_usize`, e.g. `pool.checkout({ requestTimeout: -1 })` or `Monty.create({ maxProcesses: -5 })`.","commonSituations":"Subtracting to compute a deadline that is already in the past (`now - deadline < 0`); sign errors in config math; a sentinel `-1` meaning 'default' that the API does not support.","solutions":["Clamp to 0 or omit the field to accept the library default","Fix the upstream arithmetic producing the negative value","Guard with `value >= 0` before passing the option"],"exampleFix":"// before\nconst timeout = deadlineMs - Date.now(); // may be negative\nawait session.feedRun(code, { timeout })\n// after\nconst timeout = Math.max(0, deadlineMs - Date.now());\nawait session.feedRun(code, { timeout })","handlingStrategy":"validation","validationCode":"function assertNonNegative(name, v) {\n  if (typeof v === 'number' && v < 0) throw new RangeError(`${name} must be non-negative`);\n  return v;\n}","typeGuard":"const isNonNegative = (v) => typeof v === 'number' && v >= 0 && Number.isFinite(v);","tryCatchPattern":"try {\n  await session.feedRun(code, { timeout: rawTimeout });\n} catch (e) {\n  if (/must be non-negative/.test(e?.message ?? '')) {\n    // clamp and retry with Math.max(0, rawTimeout)\n  } else throw e;\n}","preventionTips":["Clamp duration arithmetic with Math.max(0, ...)","Do not use -1 as a 'default' sentinel with this API","Check signs when deriving limits from deadlines"],"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"}