santifer/career-ops · error · Error

payload.cv requires

Error message

payload.cv requires { section, entry }

What it means

When payload.cv is present, applyAdd destructures { section, dedupKey, entry } from it and requires at minimum a non-empty `section` (which cv.md section to insert into) and an `entry` (the markdown content to add). A truthy payload.cv missing either means the caller shaped the CV part of the payload incorrectly, so there is no well-defined insertion to perform.

Solutions

  1. Provide both fields: applyAdd({ cv: { section: 'Projects', dedupKey: '...', entry: '- ...' } }).
  2. If passing a raw string, wrap it: { cv: { section: <targetHeading>, dedupKey: <uniqueKey>, entry: <markdownText> } }.
  3. Pick a section name that exactly matches an existing heading in cv.md so insertIntoCvSection can find it.

Example fix

// before
await applyAdd({ cv: 'Built cv-santiago portfolio' }, { cvText });

// after
await applyAdd(
  { cv: { section: 'Projects', dedupKey: 'proj:cv-santiago', entry: '- **cv-santiago** — open-source portfolio' } },
  { cvText }
);
Defensive patterns

Strategy: validation

Validate before calling

function isValidCvPayload(cv) {
  return Boolean(cv && cv.section && cv.entry);
}
if (payload.cv && !isValidCvPayload(payload.cv)) throw new Error('cv payload needs { section, entry }');

Type guard

function isCvTarget(cv) {
  return typeof cv === 'object' && cv !== null && typeof cv.section === 'string' && cv.section.length > 0 && typeof cv.entry === 'string' && cv.entry.length > 0;
}

Try / catch

try {
  applyAdd({ cv: cvTarget }, { cvText });
} catch (e) {
  if (e.message.includes('payload.cv requires')) {
    console.error(`Malformed cv payload: ${JSON.stringify(cvTarget)}`);
  } else throw e;
}

Prevention

When it happens

Trigger: applyAdd({ cv: { section: 'Projects' } }) (no entry), applyAdd({ cv: { entry: '...' } }) (no section), or applyAdd({ cv: { section: '', entry: '...' } }) — truthy payload.cv but empty/absent section or entry.

Common situations: Hand-writing the payload and forgetting a field, constructing { cv: 'my entry text' } (a string instead of the required object), or a refactor that moved `section`/`entry` to a different nesting level.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/2140ace8d53c317b. Report an issue: GitHub.

Appendix: source

Thrown at add-entry.mjs:180

}

/**
 * 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 === null

View on GitHub (pinned to aac998c7ed)