santifer/career-ops · error · Error

Invalid page budget "${maxPages}". Use a positive integer.

Error message

Invalid page budget "${maxPages}". Use a positive integer.

What it means

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.

Source

Thrown at generate-pdf.mjs:290

}

/**
 * Decide whether a rendered CV fits its configured page budget.
 *
 * This is deliberately separate from rendering: page count comes from the
 * PDF Chromium actually produced, and the renderer never changes layout to
 * force content under the limit.
 *
 * @param {number} pageCount - Actual pages in the rendered PDF.
 * @param {{ maxPages?: number, strictPages?: boolean }} [options]
 * @returns {void}
 */
export function enforcePageBudget(pageCount, { maxPages = 2, strictPages = false } = {}) {
  if (!Number.isInteger(pageCount) || pageCount < 1) {
    throw new Error(`Could not determine the rendered PDF page count (received ${pageCount}).`);
  }
  if (!Number.isInteger(maxPages) || maxPages < 1) {
    throw new Error(`Invalid page budget "${maxPages}". Use a positive integer.`);
  }
  if (pageCount <= maxPages) return;

  const actualLabel = 'pages';
  const allowedLabel = maxPages === 1 ? 'page' : 'pages';
  const message =
    `CV is ${pageCount} ${actualLabel}; the allowed maximum is ${maxPages} ${allowedLabel}. ` +
    'Trim lower-priority bullets, older roles, secondary projects, or the competencies strip, then regenerate.';

  if (strictPages) {
    throw new Error(`${message} (--strict-pages requested)`);
  }

  console.warn(`⚠️  ${message} Continuing because overflow is warning-only by default; use --strict-pages to reject it.`);
}

/**
 * Read the page count from the PDF catalog's root /Pages dictionary.

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Coerce and validate maxPages before calling: parse with Number(), then guard Number.isInteger(n) && n >= 1, falling back to the default of 2.
  2. If you intend "no limit", skip enforcePageBudget entirely rather than passing 0 or a sentinel.
  3. When reading from CLI, do: const maxPages = Number.isInteger(Number(arg)) ? Number(arg) : 2;
  4. Fix the upstream config so maxPages is an unquoted integer in portals.yml / profile.yml.

Example fix

// before
const maxPages = config.max_pages; // string "2" from YAML
enforcePageBudget(count, { maxPages });

// after
const raw = Number(config.max_pages);
const maxPages = Number.isInteger(raw) && raw >= 1 ? raw : 2;
enforcePageBudget(count, { maxPages });
Defensive patterns

Strategy: validation

Validate before calling

function safeMaxPages(raw) {
  const n = typeof raw === 'number' ? raw : Number(raw);
  if (!Number.isInteger(n) || n < 1) return 2; // sane default
  return n;
}
const maxPages = safeMaxPages(config.max_pages);
enforcePageBudget(count, { maxPages });

Type guard

/** True for valid page budgets accepted by enforcePageBudget. */
function isPageBudget(v) {
  return typeof v === 'number' && Number.isInteger(v) && v >= 1;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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