{"record":{"id":"18b58a3d2d6f84d4","repo":"santifer/career-ops","slug":"usage","errorCode":"USAGE","errorMessage":"Invalid appNum: ${appNum}","messagePattern":"Invalid appNum: (.+?)","errorType":"error_code","errorClass":"SeedError","httpStatus":null,"severity":"error","filePath":"followup-seed.mjs","lineNumber":545,"sourceCode":" */\nexport async function withFollowupsLock(followupsPath, fn, options = {}) {\n  const resolvedPath = resolveFollowupsPath(followupsPath);\n  const lockDir = resolveLockDir(options.lockDir, resolvedPath);\n  const lock = await acquireFollowupsLock(lockDir, resolvedPath, {\n    timeoutMs: options.timeoutMs ?? envInt('CAREER_OPS_FOLLOWUPS_LOCK_TIMEOUT_MS', 60_000),\n    retryMs: options.retryMs ?? envInt('CAREER_OPS_FOLLOWUPS_LOCK_RETRY_MS', 75),\n    staleMs: options.staleMs ?? envInt('CAREER_OPS_FOLLOWUPS_LOCK_STALE_MS', 10 * 60_000),\n  });\n  try {\n    return await fn();\n  } finally {\n    lock.release();\n  }\n}\n\nexport async function seedFollowup(appNum, options = {}) {\n  if (!Number.isInteger(appNum) || appNum <= 0) {\n    throw new SeedError('USAGE', `Invalid appNum: ${appNum}`);\n  }\n  if (options.date != null && !isValidCalendarDate(options.date)) {\n    throw new SeedError('INVALID_DATE', `--date must be a real calendar date in YYYY-MM-DD form: ${options.date}`);\n  }\n\n  const trackerPath = resolveTrackerPath(options.trackerPath);\n  const followupsPath = resolveFollowupsPath(options.followupsPath);\n\n  if (!existsSync(trackerPath)) {\n    throw new SeedError('ROW_NOT_FOUND', `Tracker not found at ${trackerPath}`);\n  }\n  const rows = readTrackerRows(trackerPath);\n  const row = rows.find(r => r.num === appNum);\n  if (!row) {\n    throw new SeedError('ROW_NOT_FOUND', `Application #${appNum} not found in ${trackerPath}`);\n  }\n\n  const normalized = normalizeStatus(row.status);","sourceCodeStart":527,"sourceCodeEnd":563,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/followup-seed.mjs#L527-L563","documentation":"seedFollowup requires the application tracker number to be a positive integer. Before doing any I/O it validates appNum and throws a SeedError with code USAGE if it is not an integer greater than 0. This is a caller-contract error: the wrong type or value was passed for the tracker row id.","triggerScenarios":"Calling seedFollowup('42') (string instead of number), seedFollowup(0), seedFollowup(-3), seedFollowup(NaN), or a float like 4.5; passing a value parsed from CLI args without Number.parseInt conversion.","commonSituations":"Script glue that passes process.argv entries directly (always strings); off-by-one generation of row numbers starting at 0; JSON payloads where the id arrived as a string from an API or TSV cell.","solutions":["Pass the tracker row number as a positive integer: seedFollowup(42).","Convert string input explicitly: seedFollowup(Number.parseInt(raw, 10)).","Guard against 0/negative/NaN values before calling.","Check the tracker table to confirm the actual row # before seeding."],"exampleFix":"// before\nawait seedFollowup(process.argv[2]);\n// after\nconst appNum = Number.parseInt(process.argv[2], 10);\nif (!Number.isInteger(appNum) || appNum <= 0) {\n  console.error('Usage: seed <appNum> where appNum is a positive integer');\n  process.exit(1);\n}\nawait seedFollowup(appNum);","handlingStrategy":"validation","validationCode":"function isValidAppNum(n) {\n  return Number.isInteger(n) && n > 0;\n}\nif (!isValidAppNum(appNum)) throw new Error(`appNum must be a positive integer, got ${appNum}`);","typeGuard":"const isAppNum = (v) => typeof v === 'number' && Number.isInteger(v) && v > 0;","tryCatchPattern":"try {\n  await seedFollowup(appNum);\n} catch (e) {\n  if (e.code === 'USAGE') {\n    console.error(`Bad appNum: ${e.message}. Pass the integer tracker row #.`);\n    process.exitCode = 2;\n  } else throw e;\n}","preventionTips":["Parse CLI args with Number.parseInt(raw, 10) instead of passing raw strings.","Validate row numbers at the boundary where they enter your script (TSV/JSON/argv).","Remember tracker rows are 1-indexed integers — never pass 0 or computed indices.","Log the value and typeof appNum when automating so type slips surface early."],"tags":["validation","arguments","cli"],"backgroundTag":"invalid-argument-value","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}