santifer/career-ops · error · Error

reportNum must be a numeric report number

Error message

reportNum must be a numeric report number

What it means

Validation in `applicationArtifactPaths`: the `reportNum` argument, coerced to string, must match `/^\d+$/` (one or more digits only — no padding, sign, decimal, or suffix). Used to build the deterministic artifact directory key, so any non-numeric input would corrupt the on-disk layout and break lookups.

Source

Thrown at application-artifacts.mjs:32

import { fileURLToPath } from 'url';

const DEFAULT_OUTPUT_ROOT = resolve('output');
const DECISIONS = new Set(['reuse', 'reuse-with-edits', 'regenerate']);

/** Convert a user-facing label into a safe, readable path segment. */
export function slugifySegment(value, fallback = 'application') {
  const slug = String(value ?? '')
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '');
  return slug || fallback;
}

/** Return all stable paths belonging to one application artifact bundle. */
export function applicationArtifactPaths({ reportNum, company, role, version = 1, root = DEFAULT_OUTPUT_ROOT }) {
  if (!/^\d+$/.test(String(reportNum ?? ''))) {
    throw new Error('reportNum must be a numeric report number');
  }
  if (!/^\d+$/.test(String(version ?? '')) || Number(version) < 1) {
    throw new Error('version must be a positive integer');
  }
  const key = `${String(reportNum).padStart(3, '0')}-${slugifySegment(company)}-${slugifySegment(role, 'role')}`;
  const applicationRoot = join(resolve(root), key);
  const tailoredRoot = join(applicationRoot, 'cv', 'tailored', `v${String(version).padStart(3, '0')}`);
  return {
    key,
    root: applicationRoot,
    jd: {
      current: join(applicationRoot, 'jd', 'current.md'),
      previous: join(applicationRoot, 'jd', 'previous.md'),
    },
    cv: {
      source: {
        html: join(applicationRoot, 'cv', 'source', 'original.html'),
        pdf: join(applicationRoot, 'cv', 'source', 'original.pdf'),

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Pass the bare integer report number as a number or numeric string: `42` or `'42'`.
  2. Strip non-digits before calling: `reportNum.replace(/\D/g, '')` (then validate it's non-empty).
  3. If you have a report filename, extract the leading digits first: `(fn.match(/^\D*(\d+)/) || [])[1]`.
  4. Guard upstream: `if (!/^\d+$/.test(String(reportNum))) throw ...`.
  5. Pad for display elsewhere (`padStart(3, '0')`) — the function pads internally; do not pre-pad with non-digit chars.

Example fix

// before
applicationArtifactPaths({ reportNum: '042-acme.md', company: 'acme', role: 'swe' }); // throws
// after
applicationArtifactPaths({ reportNum: 42, company: 'acme', role: 'swe' });
Defensive patterns

Strategy: validation

Validate before calling

function numericReportNum(n) {
  const s = String(n ?? '');
  if (!/^\d+$/.test(s)) throw new Error(`reportNum must be digits only, got: ${n}`);
  return s;
}

Type guard

function isNumericReportNum(n) {
  return /^\d+$/.test(String(n ?? ''));
}

Prevention

When it happens

Trigger: Passing `reportNum: '042-report'`, `'42.0'`, `'-1'`, `'#42'`, `undefined`/`null` (coerces to 'undefined'), `NaN`, or a report filename slug instead of the bare number.

Common situations: Caller passes the report filename (`'042-acme-2024-01-01.md'`) instead of the number; passes a padded-with-dash slug; reads the value from a regex capture that included extra chars; off-by-one in a loop producing `undefined`; using `Number.parseInt` then stringifying `NaN`.

Related errors


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