pydantic/monty · error · napi::Error
{name} must be non-negative
Error message
{name} must be non-negative What it means
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.
Source
Thrown at crates/monty-js/src/limits.rs:108
}
/// Converts a JavaScript `number` used for a size/count limit into `u64`.
///
/// JavaScript numbers are IEEE-754 doubles, so integers above `2^53 - 1`
/// cannot be represented exactly. Rejecting values outside the safe integer
/// range avoids silently rounding resource limits at the napi boundary.
///
/// Returns `Err` for non-finite, negative, fractional, or out-of-range inputs.
/// This helper does not panic.
pub(crate) fn js_number_to_u64(value: f64, name: &str) -> Result<u64> {
const JS_MAX_SAFE_INTEGER: u64 = (1_u64 << 53) - 1;
match value {
v if !v.is_finite() => Err(Error::new(
Status::InvalidArg,
format!("{name} must be a finite number"),
)),
v if v < 0.0 => Err(Error::new(Status::InvalidArg, format!("{name} must be non-negative"))),
v if v.fract() != 0.0 => Err(Error::new(Status::InvalidArg, format!("{name} must be an integer"))),
v if v > JS_MAX_SAFE_INTEGER as f64 => Err(Error::new(
Status::InvalidArg,
format!("{name} must be a safe integer (<= {JS_MAX_SAFE_INTEGER})"),
)),
v => {
#[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
let value = v as u64;
Ok(value)
}
}
}
View on GitHub (pinned to adc986b362)
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
Example fix
// before
const timeout = deadlineMs - Date.now(); // may be negative
await session.feedRun(code, { timeout })
// after
const timeout = Math.max(0, deadlineMs - Date.now());
await session.feedRun(code, { timeout }) Defensive patterns
Strategy: validation
Validate before calling
function assertNonNegative(name, v) {
if (typeof v === 'number' && v < 0) throw new RangeError(`${name} must be non-negative`);
return v;
} Type guard
const isNonNegative = (v) => typeof v === 'number' && v >= 0 && Number.isFinite(v);
Try / catch
try {
await session.feedRun(code, { timeout: rawTimeout });
} catch (e) {
if (/must be non-negative/.test(e?.message ?? '')) {
// clamp and retry with Math.max(0, rawTimeout)
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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 })`.
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- {name} must be a finite number
- {name} must be an integer
- {name} must be a safe integer (<= 9007199254740991)
- Cannot convert JS Symbol to Monty value
- MontyFileHandle position exceeds JavaScript's maximum safe i
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/b7221d5ce734eb20.
Report an issue: GitHub.