santifer/career-ops · error · Error
each entry needs a string "input" and "output"
Error message
each entry needs a string "input" and "output"
What it means
In the batch render loop, every manifest entry must be an object with string `input` and `output`. Anything else — null, a bare string, numbers, or objects missing either key — throws this per-entry error, which is recorded as that entry's failure (the batch continues with remaining entries). input/output are the only mandatory fields; format and reportNum are optional.
Source
Thrown at generate-pdf.mjs:1261
// Prepare each entry. Preserve input order in the results by carrying the
// manifest index through renderBatch; prep failures land at their own index.
let cvMarkdown = '';
try {
cvMarkdown = await readFile(resolve(workspaceRoot, 'cv.md'), 'utf-8');
} catch (err) {
if (err?.code !== 'ENOENT') throw err;
}
// One profile governs the whole batch, so the declared order is read once
// rather than per entry. Anchored to workspaceRoot for the same reason the
// 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);View on GitHub (pinned to 60398d6549)
Solutions
- Open the manifest at the failing index (the batch reports which entry failed) and give it both keys: {"input": "output/x.html", "output": "output/x.pdf"}.
- Fix generator code so optional values are filtered out rather than serialized as undefined-dropped half-entries.
- Lint the manifest before running: every element must be an object whose typeof input/output is 'string'.
Example fix
// before (manifest.json)
[
{ "input": "output/a.html", "output": "output/a.pdf" },
{ "output": "output/b.pdf" }
]
// after
[
{ "input": "output/a.html", "output": "output/a.pdf" },
{ "input": "output/b.html", "output": "output/b.pdf" }
] Defensive patterns
Strategy: validation
Validate before calling
const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));
const bad = manifest
.map((e, i) => (!e || typeof e.input !== 'string' || typeof e.output !== 'string') ? i : -1)
.filter(i => i >= 0);
if (bad.length) { console.error(`Manifest entries missing string input/output at index: ${bad.join(', ')}`); process.exit(1); } Type guard
/** @param {unknown} e */
function isManifestEntry(e) {
return typeof e === 'object' && e !== null
&& typeof /** @type {any} */ (e).input === 'string'
&& typeof /** @type {any} */ (e).output === 'string';
} Prevention
- Validate the whole manifest with the guard above before launching a batch, not entry-by-entry at 2 a.m.
- Generate manifests with a schema (JSON Schema / zod) so undefined fields fail at build time.
- Keep a known-good manifest in the repo as the structural example.
When it happens
Trigger: A JSON manifest where one entry is a leftover string ('see above'), or a template-variable miss produced {input: undefined} that JSON.stringify dropped, leaving an object with only output; hand-written manifest with a typo'd key (in/out instead of input/output).
Common situations: Manifests generated by scripts joining partial data; copy-paste editing introducing inconsistent entries; version drift where an old manifest schema used different key names.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- invalid reportNum "${entryReport}" (use the numeric report n
- Missing required field: ${context}.${key}
- invalid format "${entryFormat}" (use: ${validFormats.join(',
- output escapes the tracker workspace: ${entryOutput}
- plugin rejected: - ${result.problems.join(' - ')}
AI-assisted analysis of santifer/career-ops@60398d6549 (2026-08-20).
Data as JSON: /api/errors/736457daab2dec5d.
Report an issue: GitHub.