santifer/career-ops · error · Error
--from (${from}) must not be after --to (${to}).
Error message
--from (${from}) must not be after --to (${to}). What it means
weekly-digest.mjs rejects an inverted explicit range: when both --from and --to are supplied but from > to, it throws instead of silently returning an empty digest (which was previously indistinguishable from 'no interviews this week'). Dates compare lexicographically as ISO YYYY-MM-DD strings, which is correct only when the strict format is used.
Source
Thrown at weekly-digest.mjs:303
from,
to,
sessionsDir = DEFAULT_SESSIONS_DIR,
questionBankPath = DEFAULT_QUESTION_BANK_PATH,
} = {}) {
// Range resolution has three cases, not two:
// - neither --from nor --to given -> default current-week range (unchanged)
// - exactly one of --from/--to given -> ambiguous, hard error (was:
// silently fell back to the default range, which quietly discarded
// the one bound the caller did supply)
// - both given but from > to -> hard error (was: silently returned an
// empty digest, indistinguishable from "no interviews this week")
let range;
if (from === undefined && to === undefined) {
range = computeDefaultRange();
} else if (from === undefined || to === undefined) {
throw new Error('--from and --to must both be supplied together (or neither, to use the default current-week range).');
} else if (from > to) {
throw new Error(`--from (${from}) must not be after --to (${to}).`);
} else {
range = { from, to };
}
const allSessions = loadSessions(sessionsDir);
const sessionsInRange = allSessions.filter((s) => inRange(s.date, range.from, range.to));
const companyNames = [...new Set(sessionsInRange.map((s) => s.company))];
// "File exists" and "file has usable content" are independent questions —
// an existing-but-empty question-bank.md is a different state than a
// missing one, and the metadata (and printSummary's "present but
// unmatched" branch) needs to be able to tell them apart.
const questionBankFound = existsSync(questionBankPath);
// existsSync() succeeding doesn't guarantee readFileSync() will: the path
// could be a directory, permissions could block the read, or the file
// could be deleted between the two calls (TOCTOU). Any of those is an
// optional-data problem, not a reason to abort the whole digest — degrade
// to "no usable content" the same way a missing file does, but keepView on GitHub (pinned to 60398d6549)
Solutions
- Swap the flags so --from is the earlier ISO date
- Always use strict YYYY-MM-DD for both bounds
- Re-run with the corrected pair; the digest is read-only so there is nothing to undo
Example fix
# before node weekly-digest.mjs --from 2026-08-20 --to 2026-08-10 # after node weekly-digest.mjs --from 2026-08-10 --to 2026-08-20
Defensive patterns
Strategy: validation
Validate before calling
if (from !== undefined && to !== undefined && from > to) {
[from, to] = [to, from]; // or hard-stop: throw new Error('inverted range')
}
// Also normalize: enforce /^\d{4}-\d{2}-\d{2}$/ on both before comparing Type guard
function isOrderedIsoRange(from, to) {
const ISO = /^\d{4}-\d{2}-\d{2}$/;
return ISO.test(from) && ISO.test(to) && from <= to;
} Try / catch
try {
await runDigest({ from, to });
} catch (err) {
if (/must not be after/.test(err.message)) {
console.error('inverted range — swap the two dates');
process.exit(2);
}
throw err;
} Prevention
- Always pass strict YYYY-MM-DD strings; other formats compare unpredictably as strings
- Convention: --from is the older date (range start)
- The tool deliberately rejects instead of returning an empty digest — do not paper over with retries
When it happens
Trigger: `node weekly-digest.mjs --from 2026-08-20 --to 2026-08-10`. Also triggered by mixed formats (e.g. --to 08/16/2026) because non-ISO strings compare unpredictably and can land in the from > to branch.
Common situations: Swapping start/end when copying a command; mixed locale date habits (MM/DD vs DD/MM); typing the newer date first intending 'between these two'.
Related errors
- --from and --to must both be supplied together (or neither,
- NOT_APPLIED
- --${name} is required
- --${name} must not contain tabs or newlines
- --${name} must be a percentage (e.g. 70 or 70%), got "${v}"
AI-assisted analysis of santifer/career-ops@60398d6549 (2026-08-20).
Data as JSON: /api/errors/55d11e87885c88b3.
Report an issue: GitHub.