{"record":{"id":"af4554d2a18c7c3e","repo":"santifer/career-ops","slug":"report-number-must-be-a-positive-integer-got-nu","errorCode":null,"errorMessage":"Report number must be a positive integer, got ${num}","messagePattern":"Report number must be a positive integer, got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"reserve-report-num.mjs","lineNumber":42,"sourceCode":"import { dirname, join, resolve } from 'path';\nimport { fileURLToPath } from 'url';\nimport {\n  extractTrackerReportNumbers, parseTrackerRow, resolveColumns,\n} from './tracker-parse.mjs';\nimport {\n  acquireTrackerLock, canonicalizeTrackerPath, resolveTrackerPath, trackerLockDirFor,\n} from './tracker-utils.mjs';\n\nconst ROOT = dirname(fileURLToPath(import.meta.url));\nconst MAX_SENTINEL_AGE_MS = 4 * 60 * 60 * 1000;\nconst MAX_RETRIES = 50;\nconst MAX_COUNT = 50;\nconst RESERVATION_TOKEN = Symbol('career-ops-report-reservation-token');\n\n/** Format a report ID with a minimum width of three digits. */\nexport function formatReportNumber(num) {\n  if (!Number.isSafeInteger(num) || num < 1) {\n    throw new TypeError(`Report number must be a positive integer, got ${num}`);\n  }\n  return String(num).padStart(3, '0');\n}\n\nfunction reportsDirFor(options = {}) {\n  return resolve(options.reportsDir\n    || process.env.CAREER_OPS_REPORTS_DIR\n    || join(options.rootDir || ROOT, 'reports'));\n}\n\nfunction trackerPathFor(options = {}) {\n  return options.trackerPath\n    ? canonicalizeTrackerPath(options.trackerPath)\n    : resolveTrackerPath(options.rootDir || ROOT);\n}\n\nfunction occupiedFromReports(reportsDir) {\n  const occupied = new Set();","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/reserve-report-num.mjs#L24-L60","documentation":"formatReportNumber() is a pure formatting helper that zero-pads a report ID to three digits (e.g. 5 -> '005'). It throws a TypeError when its argument is not a safe positive integer — specifically when Number.isSafeInteger(num) is false or num < 1. This guards the sentinel filename construction (`${formatReportNumber(num)}-RESERVED.md`) from producing malformed paths.","triggerScenarios":"Calling formatReportNumber(0), formatReportNumber(-1), formatReportNumber(3.5), formatReportNumber(NaN), formatReportNumber('5'), or formatReportNumber(Number.MAX_SAFE_INTEGER + 1). Also triggered indirectly by reserveReportNumbers/releaseReportNumbers when a caller-supplied number in the array fails the same check.","commonSituations":"Passing a string from CLI argv without Number() coercion; off-by-one math producing 0; parsing a report number from a filename regex that captured a non-numeric group; loading a number from JSON config where it was stored as a string.","solutions":["Coerce and validate before calling: const n = Number(input); if (!Number.isSafeInteger(n) || n < 1) throw ...; formatReportNumber(n)","If reading from CLI argv, wrap with Number(arg) and validate in your argument parser (e.g. parseArgs with a custom validator).","If the value comes from a filename, extract only the leading digit run with /^\\(\\d+)/ and parse it.","Ensure loops that compute report numbers start at 1, not 0."],"exampleFix":"// before\nconst num = process.argv[2]; // string '5'\nformatReportNumber(num); // throws TypeError\n\n// after\nconst num = Number(process.argv[2]);\nif (!Number.isSafeInteger(num) || num < 1) {\n  throw new TypeError(`Expected positive integer report number, got ${process.argv[2]}`);\n}\nformatReportNumber(num);","handlingStrategy":"validation","validationCode":"function validateReportNumber(num) {\n  const n = Number(num);\n  if (!Number.isSafeInteger(n) || n < 1) {\n    throw new TypeError(`Report number must be a positive integer, got ${num}`);\n  }\n  return n;\n}\n// call before formatReportNumber:\nformatReportNumber(validateReportNumber(input));","typeGuard":"function isReportNumber(value) {\n  return Number.isSafeInteger(value) && value >= 1;\n}","tryCatchPattern":"try {\n  const formatted = formatReportNumber(num);\n} catch (err) {\n  if (err instanceof TypeError && err.message.includes('positive integer')) {\n    console.error(`Invalid report number input: ${num}`);\n    process.exit(2);\n  }\n  throw err;\n}","preventionTips":["Always coerce CLI argv with Number() before passing to report-number functions.","Validate at the I/O boundary (CLI parser, config loader), not at the leaf formatting function.","When parsing report numbers from filenames, capture only the leading digit run."],"tags":["validation","report-number","typeerror","argument-validation"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}