santifer/career-ops · error · Error
version must be a positive integer
Error message
version must be a positive integer
What it means
Validation in `applicationArtifactPaths`: the optional `version` (default 1) coerced to string must match `/^\d+$/` AND its numeric value must be ≥ 1. Rejects zero, negatives, decimals, and non-numeric strings, because versions index the `cv/tailored/vNNN` subdirectory and an invalid version would create an unusable path.
Source
Thrown at application-artifacts.mjs:35
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 9b17a8ac97)
Solutions
- Pass a positive integer: omit it (defaults to 1) or supply `version: 2`, `3`, etc.
- Compute the next version explicitly upstream: `const next = (maxExisting || 0) + 1;` then pass `version: next`.
- Strip non-digits and validate before calling.
- Never use zero or negative versions — the directory layout is `v001`, `v002`, … so version 1 is the first.
- Add a test for the 0, negative, and non-numeric rejection paths.
Example fix
// before
applicationArtifactPaths({ reportNum: 42, company: 'acme', role: 'swe', version: 0 }); // throws
// after
applicationArtifactPaths({ reportNum: 42, company: 'acme', role: 'swe', version: 1 }); Defensive patterns
Strategy: validation
Validate before calling
function positiveIntVersion(v) {
const n = Number(v ?? 1);
if (!Number.isInteger(n) || n < 1) throw new Error(`version must be a positive integer, got: ${v}`);
return n;
} Type guard
function isPositiveIntVersion(v) {
const n = Number(v);
return Number.isInteger(n) && n >= 1;
} Prevention
- Omit version to use the default (1); only pass explicit positive integers.
- Compute next version upstream as `(maxExisting || 0) + 1`.
- Never pass 0, negatives, decimals, or 'vN' labels.
- Test rejection of 0, -1, 1.5, 'v2'.
When it happens
Trigger: Passing `version: 0`, `version: -1`, `version: 1.5`, `version: 'v2'`, `version: ''`, `version: 'latest'`, or `version: undefined`-as-string. Each fails either the digit-only regex or the `< 1` numeric check.
Common situations: Caller defaults new versions to 0 expecting auto-increment (the function does not auto-increment); passes a 'v2'-style label; reads version from a config that allowed null; arithmetic producing 0/NaN; off-by-one in a versioning loop.
Related errors
- reportNum must be a numeric report number
- decision must be one of: ${[...DECISIONS].join(', ')}
- 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
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/287de8d0db5ae9fe.
Report an issue: GitHub.