santifer/career-ops · error · Error

version must be a positive integer

Error message

version must be a positive integer

What it means

applicationArtifactPaths validates that the version parameter is a positive integer before constructing versioned directory paths (e.g. cv/tailored/v001). It regex-checks that version is all digits and that its numeric value is >= 1, throwing this error when either check fails. This guarantees the padded vNNN directory name is always well-formed.

Solutions

  1. Pass version as a plain integer >= 1 (e.g. applicationArtifactPaths({ reportNum: 42, company: 'Acme', role: 'Engineer', version: 2 })).
  2. If the version comes as a string, strip non-digits and parse first: parseInt(String(raw).replace(/\D/g,''), 10), then check > 1 before calling.
  3. Check for a counter that starts at 0 and was never incremented, or an off-by-one where version 0 is used to mean 'first' — the first version is 1.
  4. If the value may legitimately be absent, pass undefined (the default of 1 applies) rather than null or ''.

Example fix

// before
const paths = applicationArtifactPaths({ reportNum: 42, company: 'Acme', role: 'Eng', version: 'v2' });
// after
const version = parseInt(String(rawVersion).replace(/^v/, ''), 10);
if (Number.isInteger(version) && version >= 1) {
  const paths = applicationArtifactPaths({ reportNum: 42, company: 'Acme', role: 'Eng', version });
}
Defensive patterns

Strategy: validation

Validate before calling

function isValidVersion(v) {
  return /^\d+$/.test(String(v)) && Number(v) >= 1;
}
if (!isValidVersion(version)) throw new Error(`bad version: ${JSON.stringify(version)}`);

Type guard

const isPositiveInt = (v) => Number.isInteger(Number(v)) && Number(v) >= 1 && /^\d+$/.test(String(v));

Try / catch

let paths;
try {
  paths = applicationArtifactPaths({ reportNum, company, role, version });
} catch (err) {
  if (err.message.includes('version must be')) {
    console.error(`Invalid version ${JSON.stringify(version)}, falling back to 1`);
    paths = applicationArtifactPaths({ reportNum, company, role, version: 1 });
  } else throw err;
}

Prevention

When it happens

Trigger: Calling applicationArtifactPaths with version = 0, a negative number, a non-numeric string (e.g. 'v2', 'latest', 'v'), a float like 1.5, null/undefined passed explicitly where defaults are bypassed (destructuring default only applies to undefined), or an empty string.

Common situations: Storing the version as a zero-padded string in config and passing it back without parsing ('002' actually works, but 'v002' fails); a caller reading version from a URL query param that can be '' or '0'; arithmetic producing 0 because a counter was never incremented; passing a semantic version string like '1.2'.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/287de8d0db5ae9fe. Report an issue: GitHub.

Appendix: source

Thrown at application-artifacts.mjs:36

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'),
      },
      tailored: {
        root: tailoredRoot,

View on GitHub (pinned to aac998c7ed)