santifer/career-ops · error · Error

--${name} must be a percentage (e.g. 70 or 70%), got "${v}"

Error message

--${name} must be a percentage (e.g. 70 or 70%), got "${v}"

What it means

Validation in the `optPct` helper of `buildRow`: optional percentage fields (`threshold`, `score`) must parse via `parsePct` if non-empty. Accepts forms like `70` or `70%`; anything else (e.g. `high`, `7/10`, `0.7`, `70 percent`) is rejected to keep the column numeric and comparable.

Source

Thrown at assessment-log.mjs:125

}

// --- Append (`add` subcommand) ---
export function buildRow(fields, today) {
  const req = (name) => {
    const v = String(fields[name] ?? '').trim();
    if (!v) throw new Error(`--${name} is required`);
    if (v.includes('\t') || v.includes('\n')) throw new Error(`--${name} must not contain tabs or newlines`);
    return v;
  };
  const opt = (name) => {
    const v = String(fields[name] ?? '').trim();
    if (v.includes('\t') || v.includes('\n')) throw new Error(`--${name} must not contain tabs or newlines`);
    return v || '-';
  };
  const optPct = (name) => {
    const v = String(fields[name] ?? '').trim();
    if (!v) return '-';
    if (parsePct(v) === null) throw new Error(`--${name} must be a percentage (e.g. 70 or 70%), got "${v}"`);
    return v;
  };
  return [
    today, req('company'), opt('report'), req('platform'), req('subject'),
    optPct('threshold'), optPct('score'), opt('stale') === '-' ? '' : opt('stale'),
  ].join('\t');
}

function addEntry(args) {
  const fields = {};
  for (let i = 0; i < args.length; i++) {
    const m = args[i].match(/^--(company|report|platform|subject|threshold|score|stale)$/);
    if (m) { fields[m[1]] = args[i + 1] ?? ''; i++; }
  }
  const today = new Date().toISOString().slice(0, 10);
  let row;
  try {
    row = buildRow(fields, today);

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Pass an integer percentage optionally suffixed with `%`: `--threshold 70` or `--score 85%`.
  2. Convert fractions upstream: `Math.round(frac * 100)` → then pass as `--score <n>`.
  3. Convert letter grades upstream via your own mapping before invoking.
  4. Use a comma-decimal? convert to integer first: `parseFloat('70,5'.replace(',', '.'))` → 70 or 71.
  5. Leave the flag off entirely if you don't have a value — `optPct` returns `-` for empty input.

Example fix

// before
node assessment-log.mjs add --company acme --platform leetcode --subject arrays --score 0.85
// after
node assessment-log.mjs add --company acme --platform leetcode --subject arrays --score 85
Defensive patterns

Strategy: validation

Validate before calling

function normalizePct(v) {
  if (!v) return '';
  const m = String(v).trim().match(/^(\d+(?:\.\d+)?)%?$/);
  if (!m) throw new Error(`Not a percentage: ${v}`);
  return String(Math.round(parseFloat(m[1])));
}
// convert fractions upstream: Math.round(frac * 100)

Type guard

function isPct(v) {
  return /^(\d+(?:\.\d+)?)%?$/.test(String(v ?? '').trim());
}

Prevention

When it happens

Trigger: Passing `--threshold high`, `--score 7/10`, `--threshold 0.7`, `--score 70 percent`, `--score B+`; any non-empty value for which `parsePct` returns `null`.

Common situations: User thinks in fractions (0.7) or letter grades; agent emits a qualitative label; copy-pasting from a UI that shows '70%'; locale using comma decimals (70,5); confusion between raw score and percentage.

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/bf1734712550e56b. Report an issue: GitHub.