{"record":{"id":"d481446dad4cbaac","repo":"santifer/career-ops","slug":"invalid-page-budget-maxpages-use-a-positive","errorCode":null,"errorMessage":"Invalid page budget \"${maxPages}\". Use a positive integer.","messagePattern":"Invalid page budget \"(.+?)\"\\. Use a positive integer\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"generate-pdf.mjs","lineNumber":290,"sourceCode":"}\n\n/**\n * Decide whether a rendered CV fits its configured page budget.\n *\n * This is deliberately separate from rendering: page count comes from the\n * PDF Chromium actually produced, and the renderer never changes layout to\n * force content under the limit.\n *\n * @param {number} pageCount - Actual pages in the rendered PDF.\n * @param {{ maxPages?: number, strictPages?: boolean }} [options]\n * @returns {void}\n */\nexport function enforcePageBudget(pageCount, { maxPages = 2, strictPages = false } = {}) {\n  if (!Number.isInteger(pageCount) || pageCount < 1) {\n    throw new Error(`Could not determine the rendered PDF page count (received ${pageCount}).`);\n  }\n  if (!Number.isInteger(maxPages) || maxPages < 1) {\n    throw new Error(`Invalid page budget \"${maxPages}\". Use a positive integer.`);\n  }\n  if (pageCount <= maxPages) return;\n\n  const actualLabel = 'pages';\n  const allowedLabel = maxPages === 1 ? 'page' : 'pages';\n  const message =\n    `CV is ${pageCount} ${actualLabel}; the allowed maximum is ${maxPages} ${allowedLabel}. ` +\n    'Trim lower-priority bullets, older roles, secondary projects, or the competencies strip, then regenerate.';\n\n  if (strictPages) {\n    throw new Error(`${message} (--strict-pages requested)`);\n  }\n\n  console.warn(`⚠️  ${message} Continuing because overflow is warning-only by default; use --strict-pages to reject it.`);\n}\n\n/**\n * Read the page count from the PDF catalog's root /Pages dictionary.","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/generate-pdf.mjs#L272-L308","documentation":"Thrown by enforcePageBudget() in generate-pdf.mjs when the maxPages budget option is not a positive integer. The guard runs before comparing the rendered page count against the limit, so a fractional, zero, negative, or non-numeric maxPages (e.g. \"2\", 2.5, NaN) is rejected up front rather than silently letting a CV pass or fail the comparison. It is a configuration-validation error, not a rendering error.","triggerScenarios":"Calling enforcePageBudget(pageCount, { maxPages }) or generate-pdf with a CLI/config-derived maxPages that is a string (\"2\"), a float (1.5), 0, a negative number, NaN, or undefined-coerced garbage. The Number.isInteger(maxPages) || maxPages < 1 check fails for any of these.","commonSituations":"Parsing --max-pages from argv without Number() conversion; reading maxPages from a YAML/JSON config where it landed as a quoted string; passing a default of 0 to mean \"unlimited\"; float budgets like 2.5 from a slider/UI.","solutions":["Coerce and validate maxPages before calling: parse with Number(), then guard Number.isInteger(n) && n >= 1, falling back to the default of 2.","If you intend \"no limit\", skip enforcePageBudget entirely rather than passing 0 or a sentinel.","When reading from CLI, do: const maxPages = Number.isInteger(Number(arg)) ? Number(arg) : 2;","Fix the upstream config so maxPages is an unquoted integer in portals.yml / profile.yml."],"exampleFix":"// before\nconst maxPages = config.max_pages; // string \"2\" from YAML\nenforcePageBudget(count, { maxPages });\n\n// after\nconst raw = Number(config.max_pages);\nconst maxPages = Number.isInteger(raw) && raw >= 1 ? raw : 2;\nenforcePageBudget(count, { maxPages });","handlingStrategy":"validation","validationCode":"function safeMaxPages(raw) {\n  const n = typeof raw === 'number' ? raw : Number(raw);\n  if (!Number.isInteger(n) || n < 1) return 2; // sane default\n  return n;\n}\nconst maxPages = safeMaxPages(config.max_pages);\nenforcePageBudget(count, { maxPages });","typeGuard":"/** True for valid page budgets accepted by enforcePageBudget. */\nfunction isPageBudget(v) {\n  return typeof v === 'number' && Number.isInteger(v) && v >= 1;\n}","tryCatchPattern":null,"preventionTips":["Always coerce config/CLI-derived maxPages through Number() and an integer check before calling enforcePageBudget.","Treat 0 or undefined as 'use the default (2)', not as 'unlimited' or a sentinel.","Keep max_pages an unquoted integer in YAML/JSON config.","Add a unit test that passes string/float/0/negative and asserts the guard throws with a clear message."],"tags":["validation","configuration","pdf","type-coercion"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}