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
- Pass an integer percentage optionally suffixed with `%`: `--threshold 70` or `--score 85%`.
- Convert fractions upstream: `Math.round(frac * 100)` → then pass as `--score <n>`.
- Convert letter grades upstream via your own mapping before invoking.
- Use a comma-decimal? convert to integer first: `parseFloat('70,5'.replace(',', '.'))` → 70 or 71.
- 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
- Pass integer percentages, optionally with a trailing `%`.
- Convert fractions (0.85 → 85) and letter grades upstream.
- Leave threshold/score flags off when you have no value.
- Test rejection of `0.7`, `7/10`, `high`.
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
- --${name} is required
- --${name} must not contain tabs or newlines
- Application answer state must be one of: ${[...VALID_STATES]
- Missing value for ${arg}
- payload must include at least one of: cv, articleDigest
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/bf1734712550e56b.
Report an issue: GitHub.