santifer/career-ops · error · Error
payload.cv requires { section, entry }
Error message
payload.cv requires { section, entry } What it means
Per-target validation inside `applyAdd` for the `cv` payload: when `payload.cv` is provided, it must contain both `section` and `entry`. Thrown before any dedup or I/O, because without a target section and content there is nothing to insert into `cv.md`.
Source
Thrown at add-entry.mjs:156
}
/**
* Pure core: given the current file contents and a payload, compute the new
* contents and a per-target status. No I/O — this is what the tests exercise.
* @returns {{ cv: string, articleDigest: string, result: object }}
*/
export function applyAdd(payload, { cvText = null, articleText = null } = {}) {
if (!payload || typeof payload !== 'object' || (!payload.cv && !payload.articleDigest)) {
throw new Error('payload must include at least one of: cv, articleDigest');
}
const result = {};
let cv = cvText;
let articleDigest = articleText;
if (payload.cv) {
const { section, dedupKey, entry } = payload.cv;
if (!section || !entry) throw new Error('payload.cv requires { section, entry }');
// dedupKey is what makes the insert idempotent — refuse to add without one
// rather than silently allowing duplicate re-runs.
if (!normalizeKey(dedupKey)) throw new Error('payload.cv requires a non-empty dedupKey (used for dedup/idempotency)');
if (cvText === null) throw new Error(`cv.md not found — cannot add to a CV that does not exist`);
if (cvHasEntry(cvText, section, dedupKey)) {
result.cv = { status: 'duplicate', section };
} else {
cv = insertIntoCvSection(cvText, section, entry);
result.cv = { status: 'added', section };
}
}
if (payload.articleDigest) {
const { dedupKey, entry } = payload.articleDigest;
if (!entry) throw new Error('payload.articleDigest requires { entry }');
if (!normalizeKey(dedupKey)) throw new Error('payload.articleDigest requires a non-empty dedupKey (used for dedup/idempotency)');
// article-digest.md is optional; create it from a header when missing.
const current = articleText === nullView on GitHub (pinned to 9b17a8ac97)
Solutions
- Ensure `payload.cv` includes both `section` (e.g. 'Experience', 'Projects', 'Skills') and a non-empty `entry` string.
- Add a caller-side assert: `if (!p.cv.section || !p.cv.entry) throw ...` with a clearer message before calling applyAdd.
- Double-check the section name matches an existing heading in `cv.md` so `insertIntoCvSection` can find it.
- Generate the entry string eagerly — never pass a value that may be conditionally empty.
- Cover the missing-section and missing-entry cases in unit tests.
Example fix
// before
applyAdd({ cv: { dedupKey: 'k', entry: '- Built X' } }); // throws — no section
// after
applyAdd({ cv: { section: 'Projects', dedupKey: 'x', entry: '- Built X (2024)' } }); Defensive patterns
Strategy: validation
Validate before calling
function validateCvPayload(cv) {
if (!cv || typeof cv !== 'object') return false;
return typeof cv.section === 'string' && cv.section.trim() !== '' &&
typeof cv.entry === 'string' && cv.entry.trim() !== '';
}
if (payload.cv && !validateCvPayload(payload.cv)) {
throw new Error('payload.cv needs non-empty { section, entry }');
} Type guard
function isCvEntry(c) {
return !!c && typeof c === 'object' &&
typeof c.section === 'string' && c.section.trim() !== '' &&
typeof c.entry === 'string' && c.entry.trim() !== '';
} Prevention
- Build the cv object with a typed helper that requires section + entry.
- Confirm the section name matches a real heading in cv.md so insertion succeeds.
- Generate entry text eagerly — never pass possibly-empty strings.
- Add tests covering missing-section and missing-entry.
When it happens
Trigger: Calling `applyAdd({ cv: { dedupKey: 'x', entry: '...' } })` (missing `section`); `applyAdd({ cv: { section: 'Skills' } })` (missing `entry`); `applyAdd({ cv: {} })`; or any cv sub-object where one of the two is an empty/falsy value.
Common situations: Caller builds the cv object dynamically and forgets the section key; entry text is generated conditionally and comes back empty; schema drift between the agent's mental model and the function; passing the whole entry string as `section` by mistake.
Related errors
- payload must include at least one of: cv, articleDigest
- payload.cv requires a non-empty dedupKey (used for dedup/ide
- payload.articleDigest requires { entry }
- CV section order diverges from cv.md: rendered ${renderedOrd
- Application answer state must be one of: ${[...VALID_STATES]
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/2140ace8d53c317b.
Report an issue: GitHub.