santifer/career-ops · error · Error

payload must include at least one of: cv, articleDigest

Error message

payload must include at least one of: cv, articleDigest

What it means

applyAdd() is the pure core of career-ops' `add` mode: given current cv.md / article-digest.md contents plus a payload, it computes the new contents. It refuses payloads that contain neither a `cv` nor an `articleDigest` key, since there would be nothing to insert into either target file. The throw is a fail-fast argument validation before any text transformation happens.

Solutions

  1. Pass a payload object with at least one of the two keys set: applyAdd({ cv: {...} } or applyAdd({ articleDigest: {...} }).
  2. Check the payload key spelling — `cv` and `articleDigest` are exact; nested CV content belongs under payload.cv as { section, dedupKey, entry }.
  3. Verify the code path that constructs the payload actually assigns it (an object built conditionally may stay empty when a flag is absent).

Example fix

// before
const payload = {};
if (addCv) payload.cvSection = { section: 'Projects', entry };
await applyAdd(payload, { cvText });

// after
const payload = {};
if (addCv) payload.cv = { section: 'Projects', dedupKey: 'proj:cv-santiago', entry };
await applyAdd(payload, { cvText });
Defensive patterns

Strategy: validation

Validate before calling

function canCallApplyAdd(payload) {
  return payload != null && typeof payload === 'object' && (Boolean(payload.cv) || Boolean(payload.articleDigest));
}

Type guard

function isAddPayload(p) {
  return typeof p === 'object' && p !== null && ('cv' in p || 'articleDigest' in p);
}

Try / catch

try {
  applyAdd(payload, { cvText, articleText });
} catch (e) {
  if (e.message.startsWith('payload must include')) {
    console.error('Nothing to add: set payload.cv or payload.articleDigest');
  } else throw e;
}

Prevention

When it happens

Trigger: Calling applyAdd(null), applyAdd('string'), applyAdd({}), or applyAdd({ cv: null, articleDigest: undefined }) — any call where payload is not a non-null object with a truthy cv or articleDigest property.

Common situations: Building the payload conditionally (e.g. `const payload = {}; if (opts.toCv) payload.cvSection = ...` using the wrong key name), a CLI flag parser producing an empty options object, or passing parsed JSON whose keys were renamed between versions of the add-entry API.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at add-entry.mjs:171

  }
  return false;
}

export function appendArticleDigest(md, entry) {
  const block = entry.replace(/\s+$/, '');
  const base = md.replace(/\s+$/, '');
  // Keep the existing `---`-separated block rhythm.
  return `${base}\n\n---\n\n${block}\n`;
}

/**
 * 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 };

View on GitHub (pinned to aac998c7ed)