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
- Parse and validate appNum as a positive integer before calling: const n = Number(input); if (!Number.isInteger(n) || n <= 0) throw.
- If the value comes from tracker row.num, ensure the row was found before forwarding.
- 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
- Always parse CLI input with parseInt and validate before forwarding to the API.
- Use Number.isInteger rather than typeof === 'number' to reject floats and NaN.
- Validate at the boundary (entry point), not inside every call site.
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
- a16z-speedrun-talent: unexpected API response on page ${page
- agentic-jobs: unexpected API response shape on page ${page}
- agentic-jobs: parsed 0 jobs from the API — the response shap
- payload must include at least one of: cv, articleDigest
- payload.cv requires { section, entry }
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/18b58a3d2d6f84d4.
Report an issue: GitHub.