santifer/career-ops · warning · SeedError
NOT_APPLIED
NOT_APPLIED
Error message
Application #${appNum} is not Applied (status: "${row.status.trim()}"); use --force to seed anyway What it means
The row was found but its status does not normalize to 'applied'. Follow-up seeding only makes sense for Applied rows because the cadence clock starts at application submission (applied_first offset from resolveAppliedDate). Thrown only when options.force is falsy. normalizeStatus handles lowercase, trailing dates, localized variants, and markdown bold.
Source
Thrown at followup-seed.mjs:435
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);
if (normalized !== 'applied' && !options.force) {
throw new SeedError('NOT_APPLIED', `Application #${appNum} is not Applied (status: "${row.status.trim()}"); use --force to seed anyway`);
}
const appliedDate = resolveAppliedDate(row, options.date);
const cadence = resolveCadenceConfig({ profilePath: options.profilePath });
const nextDate = addDays(parseDate(appliedDate), cadence.applied_first);
const setDate = todayStr();
const pin = formatPinLine(appNum, nextDate, setDate);
if (options.dryRun) {
const existingContent = existsSync(followupsPath) ? readFileSync(followupsPath, 'utf-8') : '';
if (isAlreadySeeded(existingContent, appNum) && !options.force) {
return { seeded: false, appNum, pin: null, nextDate, appliedDate, setDate, reason: 'already-seeded', dryRun: true };
}
return { seeded: true, appNum, pin, nextDate, appliedDate, setDate, dryRun: true };
}
const lockDir = resolveLockDir(options.lockDir, followupsPath);
const lock = await acquireFollowupsLock(lockDir, followupsPath, {View on GitHub (pinned to 9b17a8ac97)
Solutions
- Mark the row Applied first: node set-status.mjs <appNum> Applied, then seed.
- If you genuinely want to seed regardless of status, pass --force (or options.force: true).
Example fix
// before
await seedFollowup(42);
// throws: Application #42 is not Applied (status: "Evaluated")
// after — option 1: mark Applied first
// node set-status.mjs 42 Applied
await seedFollowup(42);
// after — option 2: force seed
await seedFollowup(42, { force: true }); Defensive patterns
Strategy: validation
Validate before calling
import { normalizeStatus } from './followup-cadence.mjs';
const normalized = normalizeStatus(row.status);
if (normalized !== 'applied') {
// Option A: mark Applied first via set-status.mjs
// Option B: pass force: true if you understand the implication
throw new Error(`Row is ${row.status}, not Applied. Mark Applied or use --force.`);
} Try / catch
try {
await seedFollowup(appNum);
} catch (err) {
if (err.code === 'NOT_APPLIED') {
console.error(err.message);
// Decide: mark Applied, or force seed
// await seedFollowup(appNum, { force: true });
process.exit(1);
}
throw err;
} Prevention
- Always mark a row Applied via set-status.mjs before seeding its follow-up.
- Use --force only when you understand the cadence will start from a possibly wrong date.
- Integrate seeding into the Applied-status transition workflow so it never fires on non-Applied rows.
When it happens
Trigger: Row status is 'Evaluated', 'Responded', 'Interview', 'Rejected', 'Discarded', or 'Offer'; trying to seed before marking the row Applied; the row was Applied but was since advanced to Responded.
Common situations: User tries to seed a follow-up before running set-status.mjs to mark Applied; wanting to retroactively seed a row that is now in Interview; seeding after the row was rejected.
Related errors
- payload must include at least one of: cv, articleDigest
- payload.cv requires { section, entry }
- payload.cv requires a non-empty dedupKey (used for dedup/ide
- payload.articleDigest requires { entry }
- Application answer state must be one of: ${[...VALID_STATES]
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/2b8f524cf0c425e2.
Report an issue: GitHub.