santifer/career-ops · error · Error
invalid reportNum "${entryReport}" (use the numeric report n
Error message
invalid reportNum "${entryReport}" (use the numeric report number) What it means
The optional per-entry reportNum must be all digits after trimming (/^\d+$/). It exists to tag rendered PDFs with their tracker report number, so decorated forms are rejected: 'R-064', 'report 64', '#64' all fail; '064' and '64' pass. Empty string / omitted is fine.
Source
Thrown at generate-pdf.mjs:1271
// single render is: it is the anchor readStyleTokens() and the cv.md read
// already use, so one profile.yml supplies every setting.
const cvSectionOrder = readCvSectionOrder(resolve(workspaceRoot, 'config', 'profile.yml'));
for (let i = 0; i < manifest.length; i++) {
const spec = manifest[i];
try {
if (!spec || typeof spec.input !== 'string' || typeof spec.output !== 'string') {
throw new Error('each entry needs a string "input" and "output"');
}
const entryFormat = (spec.format || globals.format).toLowerCase();
if (!validFormats.includes(entryFormat)) {
throw new Error(`invalid format "${entryFormat}" (use: ${validFormats.join(', ')})`);
}
const entryReport = (spec.reportNum ?? '').toString().trim();
if (entryReport && !/^\d+$/.test(entryReport)) {
throw new Error(`invalid reportNum "${entryReport}" (use the numeric report number)`);
}
// Resolve manifest-supplied input/output relative to the manifest's own
// directory, not process.cwd(), so a manifest renders identically wherever
// the batch is launched from. Absolute paths in the manifest still win
// (resolve() ignores the base when the tail is absolute).
const entryInput = resolve(manifestDir, spec.input);
const entryOutput = resolve(manifestDir, spec.output);
// Path-containment guards (realpath-based): keep the read and write inside
// the tracker workspace even through a symlinked ancestor. A batch
// manifest that escapes the workspace is malformed/tampered and is
// recorded as a per-entry failure rather than read or written.
assertInsideWorkspace(entryInput, 'input');
if (!isWorkspaceOutputPath(entryOutput, workspaceRoot)) {
throw new Error(`output escapes the tracker workspace: ${entryOutput}`);
}
View on GitHub (pinned to 60398d6549)
Solutions
- Use the bare numeric report number: "reportNum": "064".
- Strip decorations in the generator: `String(ref).replace(/[^0-9]/g, '')` before writing the manifest.
- If the entry has no associated report, omit reportNum entirely rather than writing a placeholder.
Example fix
// before
{ "input": "x.html", "output": "x.pdf", "reportNum": "R-064" }
// after
{ "input": "x.html", "output": "x.pdf", "reportNum": "064" } Defensive patterns
Strategy: validation
Validate before calling
const bad = manifest
.filter(e => e.reportNum !== undefined && e.reportNum !== '' && !/^\d+$/.test(String(e.reportNum).trim()))
.map(e => JSON.stringify(e.reportNum));
if (bad.length) { console.error(`reportNum must be bare digits, got: ${bad.join(', ')}`); process.exit(1); } Type guard
/** @param {unknown} v */
function isValidReportNum(v) {
if (v === undefined || v === null || v === '') return true; // optional
return /^\d+$/.test(String(v).trim());
} Prevention
- Normalize report ids in generators: String(ref).replace(/[^0-9]/g, '') before writing the manifest.
- Copy report numbers from the tracker's # column, not from decorated UI strings.
- Omit reportNum when an entry has no tracked report — never use a placeholder.
When it happens
Trigger: A manifest generator writes report references as 'report-064' or '#064' from UI-facing strings; someone pastes the report column including its markdown link syntax; negative or zero-prefixed-with-letter ids from an external tracker export.
Common situations: Glue code between an ATS/dashboard and the batch renderer that formats ids for display; manifests assembled by hand from tracker notes.
Related errors
- each entry needs a string "input" and "output"
- invalid format "${entryFormat}" (use: ${validFormats.join(',
- output escapes the tracker workspace: ${entryOutput}
- plugin rejected: - ${result.problems.join(' - ')}
- Invalid or blocked URL: ${rejected.reason}
AI-assisted analysis of santifer/career-ops@60398d6549 (2026-08-20).
Data as JSON: /api/errors/fd3a6645377af067.
Report an issue: GitHub.