santifer/career-ops · error · RangeError
Reservation count must be an integer from 1 to
Error message
Reservation count must be an integer from 1 to ${MAX_COUNT} What it means
reserveReportNumbers(count) validates its count argument before doing any work: it must be an integer between 1 and MAX_COUNT (50). A RangeError is thrown immediately when count is a non-integer (e.g. a float or a numeric string coming from CLI parsing), zero, negative, or greater than 50. This is an upfront argument guard so the allocator never attempts an impossible reservation.
Solutions
- Clamp and validate count before calling: const n = Math.floor(Number(raw)); if (Number.isInteger(n) && n >= 1 && n <= 50) await reserveReportNumbers(n);
- When you need more than 50 IDs, make multiple sequential reserveReportNumbers calls (e.g. two calls of 40) instead of one oversized call.
- Convert CLI/env strings with Number() or parseInt(raw, 10) and check Number.isInteger before passing them.
- Default to the documented count=1 when the input is not a usable integer.
Example fix
// before
const count = process.env.FANOUT;
const ids = await reserveReportNumbers(count); // throws: '8' is not an integer
// after
const count = Number.parseInt(process.env.FANOUT ?? '1', 10);
if (!Number.isInteger(count) || count < 1 || count > 50) {
throw new Error(`FANOUT must be an integer 1-50, got ${process.env.FANOUT}`);
}
const ids = await reserveReportNumbers(count); Defensive patterns
Strategy: validation
Validate before calling
function toCount(raw) {
const n = Number.parseInt(raw, 10);
if (!Number.isInteger(n) || n < 1 || n > 50) {
throw new RangeError(`count must be an integer 1-50, got ${raw}`);
}
return n;
}
const ids = await reserveReportNumbers(toCount(userInput)); Type guard
const isValidCount = (v) => Number.isInteger(v) && v >= 1 && v <= 50;
Try / catch
let ids;
try {
ids = await reserveReportNumbers(count);
} catch (err) {
if (err instanceof RangeError && /Reservation count/.test(err.message)) {
ids = await reserveReportNumbers(1); // safe default
} else throw err;
} Prevention
- Always parse CLI/env values with Number.parseInt(x, 10) before passing them as count.
- Validate with Number.isInteger — numeric strings and NaN both fail it.
- Cap fan-out at 50 workers; split larger batches into multiple reservations.
- Default to 1 (the documented parameter default) when input is missing.
When it happens
Trigger: Calling reserveReportNumbers(0), reserveReportNumbers(-3), reserveReportNumbers(51), reserveReportNumbers(2.5), or passing a string parsed from --count without Number conversion (Number.isInteger('8') is false). Also happens when a config value like a batch-size env var is NaN or undefined and defaults are bypassed.
Common situations: CLI flag mishandling: `--count` read as a raw string or parseFloat result like 8.5. Config mistakes: an env var CAREER_OPS_BATCH_SIZE set to '0' or '100'. Off-by-one math: computing count as numbers.length when the array is empty (0). Automation scripts fanning out more than the documented 50-worker cap.
Related errors
- apify: invalid actorId
- garena: is not a usable URL segment
- isMainModule expects import.meta.url (a file: URL), got a…
- isMainModule expects import.meta.url; got
- landingjobs: invalid URL
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/26a4ed6fd9adbb92.
Report an issue: GitHub.
Appendix: source
Thrown at reserve-report-num.mjs:172
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
try {
process.kill(pid, 0);
return true;
} catch (err) {
return err?.code === 'EPERM';
}
}
/**
* Reserve one or more contiguous report IDs.
*
* @param {number} [count=1] Number of IDs to reserve (1-50).
* @param {object} [options] Path and lock overrides.
* @returns {Promise<number[]>} Reserved numeric IDs.
*/
export async function reserveReportNumbers(count = 1, options = {}) {
if (!Number.isInteger(count) || count < 1 || count > MAX_COUNT) {
throw new RangeError(`Reservation count must be an integer from 1 to ${MAX_COUNT}`);
}
const reportsDir = reportsDirFor(options);
const trackerPath = trackerPathFor(options);
mkdirSync(reportsDir, { recursive: true });
const lock = await acquireTrackerLock(trackerLockDirFor(trackerPath), {
timeoutMs: Number(process.env.CAREER_OPS_TRACKER_LOCK_TIMEOUT_MS) || 60_000,
retryMs: Number(process.env.CAREER_OPS_TRACKER_LOCK_RETRY_MS) || 75,
staleMs: Number(process.env.CAREER_OPS_TRACKER_LOCK_STALE_MS) || 10 * 60_000,
tracker: trackerPath,
...options.lockOptions,
});
try {
let occupied = collectOccupied(reportsDir, trackerPath);
let base = highestNumber(occupied) + 1;
const token = randomUUID();View on GitHub (pinned to aac998c7ed)