koala73/worldmonitor · error
${label} must be between 0 and 1 million with at most 6 deci
Error message
${label} must be between 0 and 1 million with at most 6 decimal places. What it means
The quantity() helper validates that each quantity is a finite number between 0 and 1,000,000 that equals its own 6-decimal rounding (round(value) === value). It throws "${label} must be between 0 and 1 million with at most 6 decimal places." when the value is non-numeric, negative, too large, non-finite, or has more than 6 decimal places.
Solutions
- Convert to a number and clamp/round to at most 6 decimal places: Math.round(v * 1e6) / 1e6.
- Check 0 <= v <= 1e6 and Number.isFinite(v) before submitting.
- Parse numeric strings with Number() and reject NaN upstream.
Example fix
// before
parseOperationalInput({ ..., initialStock: '12.5' });
// after
const v = Number('12.5');
parseOperationalInput({ ..., initialStock: Math.round(v * 1e6) / 1e6 }); Defensive patterns
Strategy: validation
Validate before calling
function validQuantity(v: unknown): v is number {
return typeof v === 'number' && Number.isFinite(v) && v >= 0 && v <= 1e6 &&
Math.round(v * 1e6) / 1e6 === v;
} Type guard
const isQuantity = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v) && v >= 0 && v <= 1e6 && Math.round(v * 1e6) / 1e6 === v;
Try / catch
try {
const input = parseOperationalInput(raw);
} catch (e) {
if (e instanceof Error && e.message.includes('between 0 and 1 million')) {
console.error('Quantity out of range or too precise:', e.message);
} else throw e;
} Prevention
- Number() coerce form/spreadsheet values before submitting; reject NaN.
- Clamp inputs to [0, 1e6] and round to 6 decimals in the UI.
- Set step="0.000001" max="1000000" min="0" on number inputs.
- Avoid computing quantities with divisions that produce long decimals without rounding.
When it happens
Trigger: Passing NaN/Infinity, a numeric string like "12.5", a negative value, a value > 1e6, or a high-precision value like 0.1234567 for a quantity field.
Common situations: Sending form values as strings without Number() conversion; dividing quantities in ways that produce long decimals; entering micro-quantities with excessive precision; spreadsheet cells imported as text.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- digestHour must be an integer 0–23
- quietHoursStart must be an integer 0–23
- quietHoursEnd must be an integer 0–23
- year must be 0 or between 1951 and ${currentYear}
- ${label} HTTP 400
AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15).
Data as JSON: /api/errors/3108f4905efd0b89.
Report an issue: GitHub.
Appendix: source
Thrown at src/utils/operational-balance.ts:17
import type { OperationalBalance, OperationalDelivery, OperationalInput, OperationalSnapshot } from '@/types/operational-balance';
export const MAX_OPERATIONAL_DAYS = 90;
export const MAX_OPERATIONAL_DELIVERIES = 30;
export const MAX_OPERATIONAL_JSON_BYTES = 65_536;
const DAY_MS = 86_400_000;
const round = (value: number) => Math.round(value * 1e6) / 1e6;
const dateAt = (start: string, offset: number) => new Date(Date.parse(start) + offset * DAY_MS).toISOString().slice(0, 10);
function record(value: unknown): Record<string, unknown> {
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Expected a worksheet object.');
return value as Record<string, unknown>;
}
function quantity(value: unknown, label: string): number {
if (value === '' || value === null || value === undefined) throw new Error(`${label} is required.`);
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0 || value > 1e6 || round(value) !== value) {
throw new Error(`${label} must be between 0 and 1 million with at most 6 decimal places.`);
}
return value;
}
function date(value: unknown): string {
if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value) || !Number.isFinite(Date.parse(value)) || new Date(value).toISOString().slice(0, 10) !== value || value < '1900-01-01' || value > '9998-12-31') {
throw new Error('Use a valid date from 1900 through 9998.');
}
return value;
}
export function parseOperationalInput(value: unknown): OperationalInput {
const input = record(value);
if (typeof input.operation !== 'string' || !input.operation.trim() || input.operation.length > 100) throw new Error('Enter an operation name of 1-100 characters.');
if (typeof input.unit !== 'string' || !/^[\p{L}\p{N} %./-]{1,24}$/u.test(input.unit) || !input.unit.trim()) throw new Error('Enter one quantity unit of 1-24 characters.');
const unit = input.unit.trim();
const startDate = date(input.startDate);
if (!Number.isInteger(input.horizonDays) || (input.horizonDays as number) < 1 || (input.horizonDays as number) > MAX_OPERATIONAL_DAYS) throw new Error('Horizon must be 1-90 whole days.');
const horizonDays = input.horizonDays as number;View on GitHub (pinned to 7d06c8633d)