santifer/career-ops · error · Error
-- must be a percentage (e.g. 70 or 70%), got
Error message
--${name} must be a percentage (e.g. 70 or 70%), got "${v}" What it means
optPct() in buildRow() validates the --threshold and --score fields, which must be percentages (70 or 70%). It delegates to parsePct(); when parsePct returns null (unparseable value), the error is thrown quoting the offending input. This keeps the TSV columns machine-parseable so assessment-log.mjs --summary can compute percentage statistics.
Solutions
- Re-run with a valid percentage form: a bare number (70) or number with % (70%).
- Convert non-percent scales first: fraction 0.7 → 70; 'A' grade → find the numeric percentage on the platform.
- Use a decimal point, not a comma, if the value is fractional (check parsePct's accepted form).
- Omit the flag entirely if no percentage exists — optPct() returns '-' when empty instead of throwing.
Example fix
// before node assessment-log.mjs add --company Acme --platform leetcode --subject dp --score 8/10 // Error: --score must be a percentage (e.g. 70 or 70%), got "8/10" // after node assessment-log.mjs add --company Acme --platform leetcode --subject dp --score 80%
Defensive patterns
Strategy: validation
Validate before calling
const isPct = (v) => { const s = String(v ?? '').trim(); return /^\d+(\.\d+)?%?$/.test(s); };
if (fields.threshold != null && fields.threshold !== '' && !isPct(fields.threshold)) throw new Error(`--threshold must be a percentage, got "${fields.threshold}"`);
if (fields.score != null && fields.score !== '' && !isPct(fields.score)) throw new Error(`--score must be a percentage, got "${fields.score}"`); Type guard
const isPercentage = (v) => /^\d+(\.\d+)?%?$/.test(String(v).trim());
Try / catch
try {
const row = buildRow(fields, today);
} catch (err) {
const m = err.message.match(/^--(\w+) must be a percentage .* got "(.*)"$/);
if (m) {
const [, flag, bad] = m;
console.error(`Convert ${flag} value "${bad}" to a number out of 100 (e.g. 70 or 70%).`);
} else throw err;
} Prevention
- Always express assessment results on a 0-100 scale (70, not 0.7, not 8/10, not letter grades).
- Use a decimal point, never a comma, for fractional percentages.
- Omit --threshold/--score when no percentage exists rather than improvising a value.
- Validate with a quick regex before scripting bulk `add` calls.
When it happens
Trigger: Calling buildRow() with --threshold or --score set to a non-percentage string, e.g. `--threshold seventy`, `--score 70/100`, `--score "70 %"`, `--threshold 1.5x`, or any value parsePct() cannot interpret as a bare number or number-with-percent-sign.
Common situations: Entering a fraction (0.7) where the tool expects 70; writing '70 percent' in words; a score out of the platform's 0-100 convention like 'A' or 'pass'; locale-decimal input ('70,5'); typos ('7O' with letter O); passing other numeric scales (Codewars kyu, HackerRank stars) that are not percentages.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Application Answers section has
- --from and --to must both be supplied together (or neither…
- --from ( ) must not be after --to ( ).
- Invalid page budget " ". Use a positive integer.
- --limit must be an integer from 1 to 100
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/bf1734712550e56b.
Report an issue: GitHub.
Appendix: source
Thrown at assessment-log.mjs:138
}
// --- 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++; }
}
// LOCAL day: this is the date written into the appended assessments.tsv row,
// and the UTC day stamped a user record with a day that had not happened yet
// for anyone west of Greenwich (#3070).
const today = localToday();View on GitHub (pinned to aac998c7ed)