santifer/career-ops · critical · RangeError
No safe report-number range remains
Error message
No safe report-number range remains
What it means
After scanning occupied report numbers (report files, sentinels, tracker rows/links), the allocator computes base = maxOccupied + 1 and the range end. If base or end exceeds Number.MAX_SAFE_INTEGER, no safe contiguous range exists, so it throws this RangeError rather than silently allocating unsafe numeric IDs. In practice this fires only when occupied numbers are astronomically large.
Solutions
- Inspect reports/ for files whose names start with huge digit prefixes and move/rename them out of the directory (they are treated as occupied report numbers).
- Set options.reportsDir / CAREER_OPS_REPORTS_DIR to the correct dedicated reports directory instead of one polluted with timestamp-named files.
- Check data/applications.md for rows with absurd num values or report links like [20250601](reports/2025-06-01...) and fix them.
- Note the allocator already excludes bare date files (YYYY-MM-DD.md) but not timestamp-prefixed names — rename those files to not start with digits.
Example fix
// before $ cp /tmp/scan-2026-09-16T10-00-00.md reports/ // name starts with digits -> occupied 2026... // after $ mv /tmp/scan-2026-09-16T10-00-00.md reports/scan-digest-2026-09-16.md // no leading digits
Defensive patterns
Strategy: validation
Validate before calling
import { readdirSync } from 'fs';
const bad = readdirSync('reports')
.map(name => ({ name, m: name.match(/^(\d+)-/) }))
.filter(({ m }) => m && parseInt(m[1], 10) > 1e9);
if (bad.length) throw new Error(`reports/ has non-report huge-number files: ${bad.map(b => b.name)}`); Try / catch
try {
ids = await reserveReportNumbers(count);
} catch (err) {
if (err instanceof RangeError && err.message.includes('No safe report-number range')) {
// inspect reports/ and tracker for huge digit-prefixed filenames/rows
}
throw err;
} Prevention
- Never drop files whose names start with large digit runs into reports/ — they count as occupied report numbers.
- Keep CAREER_OPS_REPORTS_DIR pointed at the dedicated reports directory, not a general dump.
- Audit the tracker for absurd row numbers or report links.
- Remember bare YYYY-MM-DD.md files are excluded but timestamp-prefixed names are not.
When it happens
Trigger: A reports/ directory or tracker containing a filename whose leading digits parse to a number near Number.MAX_SAFE_INTEGER (e.g. a timestamped filename like 1789569700000-foo.md matching /^(\d+)-/), making highestNumber(occupied) enormous; the very first iteration then fails the isSafeInteger check.
Common situations: Non-report files dropped into reports/ whose names start with epoch-millisecond timestamps or large IDs. A misconfigured CAREER_OPS_REPORTS_DIR pointing at a directory of timestamp-named logs. Corrupted tracker rows with huge num or report-link numbers.
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
- apify: JD cache write failed for
- cannot read
- Could not release report reservation
- ⚠️ Could not release report reservation
- ⚠️ Could not release report reservation
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/16b1cc55eab9b587.
Report an issue: GitHub.
Appendix: source
Thrown at reserve-report-num.mjs:195
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();
for (let tries = 0; tries < MAX_RETRIES; tries++) {
const end = base + (count - 1);
if (!Number.isSafeInteger(base) || !Number.isSafeInteger(end)) {
throw new RangeError('No safe report-number range remains');
}
const claimed = [];
let failedAt = null;
for (let num = base; num <= end; num++) {
if (claimSlot(reportsDir, num, occupied, token)) {
claimed.push(num);
} else {
failedAt = num;
break;
}
}
if (failedAt == null) {
Object.defineProperty(claimed, RESERVATION_TOKEN, { value: token });
return claimed;
}
for (const num of claimed) releaseSlot(reportsDir, num, { token });
occupied = collectOccupied(reportsDir, trackerPath);View on GitHub (pinned to aac998c7ed)