{"record":{"id":"35d7fe68aada8eeb","repo":"santifer/career-ops","slug":"invalid-date","errorCode":"INVALID_DATE","errorMessage":"Application #${row?.num ?? '?'} notes carry an impossible \"Applied ${notesDate}\" date; fix the notes or pass --date","messagePattern":"Application #(.+?) notes carry an impossible \"Applied (.+?)\" date; fix the notes or pass --date","errorType":"error_code","errorClass":"SeedError","httpStatus":null,"severity":"error","filePath":"followup-seed.mjs","lineNumber":177,"sourceCode":" * `today` remains the last resort for a row whose `date` column is missing or\n * unusable — but it is now labelled too, rather than being indistinguishable\n * from a measured date.\n *\n * A notes date that isn't a real calendar date (e.g. \"Applied 2026-02-31\")\n * throws rather than falling through: `parseDate` would return null for it and\n * the pin would be written with a literal \"null\" next-date.\n *\n * @param {{num?: number, notes?: string, date?: string}} row - Parsed tracker row.\n * @param {string|null|undefined} explicitDate - `--date` value, already validated.\n * @returns {{appliedDate: string, appDateSource: 'explicit'|'notes'|'evaluation-date'|'today'}}\n * @throws {SeedError} INVALID_DATE when the notes carry an impossible date.\n */\nexport function resolveAppliedDate(row, explicitDate) {\n  if (explicitDate) return { appliedDate: explicitDate, appDateSource: 'explicit' };\n  const notesDate = parseAppliedDate(row?.notes);\n  if (notesDate) {\n    if (!isValidCalendarDate(notesDate)) {\n      throw new SeedError('INVALID_DATE', `Application #${row?.num ?? '?'} notes carry an impossible \"Applied ${notesDate}\" date; fix the notes or pass --date`);\n    }\n    return { appliedDate: notesDate, appDateSource: 'notes' };\n  }\n  // Only a usable calendar date qualifies: a blank or malformed `date` cell\n  // must not become a pin with a \"null\" next-date, which is the same failure\n  // the notes branch throws over.\n  const columnDate = String(row?.date ?? '').trim();\n  if (isValidCalendarDate(columnDate)) {\n    return { appliedDate: columnDate, appDateSource: 'evaluation-date' };\n  }\n  return { appliedDate: todayStr(), appDateSource: 'today' };\n}\n\n/**\n * Format one pin directive line. The parser side lives in\n * followup-cadence.mjs's `OVERRIDE_RE` / `parseNextOverrides`.\n *\n * @param {number} appNum","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/followup-seed.mjs#L159-L195","documentation":"resolveAppliedDate derives an application's Applied date for seeding the first follow-up: from an explicit --date, else from an 'Applied YYYY-MM-DD' pattern in the tracker row's notes, else from the row's date cell. When the notes contain an Applied date string that parses but is not a valid calendar date (e.g. 2026-02-30, month 13), it throws a SeedError with code INVALID_DATE rather than pinning a nonsense follow-up date.","triggerScenarios":"Running followup-seed (or a mode that seeds follow-ups) without --date on a tracker row whose notes contain something like 'Applied 2025-02-30' or 'Applied 2025-13-01' — a syntactically date-like but calendar-impossible value that parseAppliedDate extracts and isValidCalendarDate rejects.","commonSituations":"Typo when hand-writing notes ('Applied 2025-04-31'); swapped day/month after converting from US format (2025-04-13 vs 13th month); row duplicated with an edited but broken date; notes written by a script with an unvalidated computed date.","solutions":["Fix the notes column of the named application row so the 'Applied <date>' value is a real calendar date, then re-run.","Pass an explicit --date YYYY-MM-DD to override the notes value for this run.","If the date cell (not the notes) is the intended source, remove the bogus 'Applied ...' text from notes so resolution falls through to the date cell.","Update via node set-status.mjs <row> <State> --note ... to rewrite notes through the canonical path instead of hand-editing the table.","Add a date sanity check to any script that writes 'Applied' notes to prevent recurrence."],"exampleFix":"// before: tracker row notes\n| 42 | 2025-09-01 | Acme | Backend | — | Applied | ❌ | [42](reports/042-acme.md) | Applied 2025-02-30, emailed recruiter | //\n\n// after: corrected notes (2025-02-28)\n| 42 | 2025-09-01 | Acme | Backend | — | Applied | ❌ | [42](reports/042-acme.md) | Applied 2025-02-28, emailed recruiter |","handlingStrategy":"validation","validationCode":"function isRealCalendarDate(s) {\n  const m = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(s ?? '');\n  if (!m) return false;\n  const d = new Date(Date.UTC(+m[1], +m[2] - 1, +m[3]));\n  return d.getUTCFullYear() === +m[1] && d.getUTCMonth() === +m[2] - 1 && d.getUTCDate() === +m[3];\n}\n// check notes for /Applied (\\d{4}-\\d{2}-\\d{2})/ and run isRealCalendarDate before seeding","typeGuard":null,"tryCatchPattern":"try {\n  const { appliedDate } = resolveAppliedDate(row, cliDate);\n} catch (err) {\n  if (err.code === 'INVALID_DATE') {\n    console.error(`${err.message}\\nFix the row's notes or pass --date YYYY-MM-DD`);\n    process.exit(1);\n  }\n  throw err;\n}","preventionTips":["Verify Applied dates in notes with a calendar check (Feb 30 / month 13 are the classic slips)","Use node set-status.mjs --note to rewrite notes instead of hand-editing the tracker table","Pass an explicit --date when the notes date is suspect rather than letting resolution guess","Standardize on ISO YYYY-MM-DD everywhere to avoid day/month swap errors","Add a tracker lint that flags date-like strings in notes failing isValidCalendarDate"],"tags":["date","validation","tracker","data-integrity"],"backgroundTag":"invalid-date-format","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"}