{"record":{"id":"287de8d0db5ae9fe","repo":"santifer/career-ops","slug":"version-must-be-a-positive-integer","errorCode":null,"errorMessage":"version must be a positive integer","messagePattern":"version must be a positive integer","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"application-artifacts.mjs","lineNumber":36,"sourceCode":"const DECISIONS = new Set(['reuse', 'reuse-with-edits', 'regenerate']);\n\n/** Convert a user-facing label into a safe, readable path segment. */\nexport function slugifySegment(value, fallback = 'application') {\n  const slug = String(value ?? '')\n    .trim()\n    .toLowerCase()\n    .replace(/[^a-z0-9]+/g, '-')\n    .replace(/^-+|-+$/g, '');\n  return slug || fallback;\n}\n\n/** Return all stable paths belonging to one application artifact bundle. */\nexport function applicationArtifactPaths({ reportNum, company, role, version = 1, root = DEFAULT_OUTPUT_ROOT }) {\n  if (!/^\\d+$/.test(String(reportNum ?? ''))) {\n    throw new Error('reportNum must be a numeric report number');\n  }\n  if (!/^\\d+$/.test(String(version ?? '')) || Number(version) < 1) {\n    throw new Error('version must be a positive integer');\n  }\n  const key = `${String(reportNum).padStart(3, '0')}-${slugifySegment(company)}-${slugifySegment(role, 'role')}`;\n  const applicationRoot = join(resolve(root), key);\n  const tailoredRoot = join(applicationRoot, 'cv', 'tailored', `v${String(version).padStart(3, '0')}`);\n  return {\n    key,\n    root: applicationRoot,\n    jd: {\n      current: join(applicationRoot, 'jd', 'current.md'),\n      previous: join(applicationRoot, 'jd', 'previous.md'),\n    },\n    cv: {\n      source: {\n        html: join(applicationRoot, 'cv', 'source', 'original.html'),\n        pdf: join(applicationRoot, 'cv', 'source', 'original.pdf'),\n      },\n      tailored: {\n        root: tailoredRoot,","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/application-artifacts.mjs#L18-L54","documentation":"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.","triggerScenarios":"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.","commonSituations":"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'.","solutions":["Pass version as a plain integer >= 1 (e.g. applicationArtifactPaths({ reportNum: 42, company: 'Acme', role: 'Engineer', version: 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.","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.","If the value may legitimately be absent, pass undefined (the default of 1 applies) rather than null or ''."],"exampleFix":"// before\nconst paths = applicationArtifactPaths({ reportNum: 42, company: 'Acme', role: 'Eng', version: 'v2' });\n// after\nconst version = parseInt(String(rawVersion).replace(/^v/, ''), 10);\nif (Number.isInteger(version) && version >= 1) {\n  const paths = applicationArtifactPaths({ reportNum: 42, company: 'Acme', role: 'Eng', version });\n}","handlingStrategy":"validation","validationCode":"function isValidVersion(v) {\n  return /^\\d+$/.test(String(v)) && Number(v) >= 1;\n}\nif (!isValidVersion(version)) throw new Error(`bad version: ${JSON.stringify(version)}`);","typeGuard":"const isPositiveInt = (v) => Number.isInteger(Number(v)) && Number(v) >= 1 && /^\\d+$/.test(String(v));","tryCatchPattern":"let paths;\ntry {\n  paths = applicationArtifactPaths({ reportNum, company, role, version });\n} catch (err) {\n  if (err.message.includes('version must be')) {\n    console.error(`Invalid version ${JSON.stringify(version)}, falling back to 1`);\n    paths = applicationArtifactPaths({ reportNum, company, role, version: 1 });\n  } else throw err;\n}","preventionTips":["Always pass version as a plain integer, never a 'v'-prefixed or zero-padded string.","Coerce query/config string inputs with parseInt before calling.","Remember the default is 1 — pass undefined rather than null/'' when the value is unknown.","Unit-test path builders with boundary inputs (0, -1, '1.5', '')."],"tags":["validation","arguments","path-building"],"backgroundTag":"invalid-argument-value","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"}