santifer/career-ops · error · SeedError

USAGE

USAGE

Error message

Invalid appNum: ${appNum}

What it means

seedFollowup(appNum) is the programmatic API entry point and requires a positive integer for appNum. This USAGE guard fires when appNum is a float, zero, negative, NaN, a string, or undefined. The CLI parser does its own validation separately (error 52); this guard protects direct module importers.

Source

Thrown at followup-seed.mjs:415

 * without touching the file, unless `options.force` is set.
 *
 * @param {number} appNum
 * @param {object} [options]
 * @param {string} [options.date] - Explicit apply date (YYYY-MM-DD), already validated.
 * @param {boolean} [options.force] - Bypass idempotency guard and the Applied-status guard.
 * @param {boolean} [options.dryRun] - Compute and report, but write nothing (no lock taken).
 * @param {string} [options.trackerPath]
 * @param {string} [options.followupsPath]
 * @param {string} [options.profilePath]
 * @param {string} [options.lockDir]
 * @param {number} [options.lockTimeoutMs]
 * @param {number} [options.lockRetryMs]
 * @param {number} [options.lockStaleMs]
 * @returns {Promise<object>}
 */
export async function seedFollowup(appNum, options = {}) {
  if (!Number.isInteger(appNum) || appNum <= 0) {
    throw new SeedError('USAGE', `Invalid appNum: ${appNum}`);
  }
  if (options.date != null && !isValidCalendarDate(options.date)) {
    throw new SeedError('INVALID_DATE', `--date must be a real calendar date in YYYY-MM-DD form: ${options.date}`);
  }

  const trackerPath = resolveTrackerPath(options.trackerPath);
  const followupsPath = resolveFollowupsPath(options.followupsPath);

  if (!existsSync(trackerPath)) {
    throw new SeedError('ROW_NOT_FOUND', `Tracker not found at ${trackerPath}`);
  }
  const rows = readTrackerRows(trackerPath);
  const row = rows.find(r => r.num === appNum);
  if (!row) {
    throw new SeedError('ROW_NOT_FOUND', `Application #${appNum} not found in ${trackerPath}`);
  }

  const normalized = normalizeStatus(row.status);

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Parse and validate appNum as a positive integer before calling: const n = Number(input); if (!Number.isInteger(n) || n <= 0) throw.
  2. If the value comes from tracker row.num, ensure the row was found before forwarding.
  3. Use the exported isValidCalendarDate-style discipline: validate at the boundary, not inside the library.

Example fix

// before
await seedFollowup(userInput);

// after
const n = Number(userInput);
if (!Number.isInteger(n) || n <= 0) throw new Error(`appNum must be a positive integer, got ${userInput}`);
await seedFollowup(n);
Defensive patterns

Strategy: validation

Validate before calling

function isValidAppNum(n) {
  return Number.isInteger(n) && n > 0;
}

if (!isValidAppNum(appNum)) {
  throw new Error(`appNum must be a positive integer, got ${appNum}`);
}
await seedFollowup(appNum);

Type guard

/** @param {unknown} v */
function isValidAppNum(v) {
  return typeof v === 'number' && Number.isInteger(v) && v > 0;
}

Prevention

When it happens

Trigger: Calling seedFollowup(0), seedFollowup(-1), seedFollowup(3.5), seedFollowup('abc'), or seedFollowup(NaN) from a test or wrapper script; passing a raw unparsed CLI string into the API; passing a row.num that is undefined because the row lookup returned nothing.

Common situations: A wrapper script forwards user input as a string instead of a parsed integer; off-by-one produces 0; parseInt returned NaN and was forwarded unchecked.

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/18b58a3d2d6f84d4. Report an issue: GitHub.