affaan-m/ECC · error · Error
Invalid now timestamp
Error message
Invalid now timestamp: ${now} What it means
collectSkillHealth takes an options.now timestamp used as the reference point for staleness and warning computations. The value is parsed with Date.parse; an unparseable value yields NaN which would make every age comparison unreliable, so the function throws before doing any work.
Solutions
- Pass an ISO 8601 string: collectSkillHealth({ now: new Date().toISOString() }).
- Validate before calling: Number.isNaN(Date.parse(value)) check in the caller with a clear message.
- Convert Date objects to strings first: now: dateObj.toISOString().
- Omit `now` so the function defaults to the current time.
Example fix
// before
health.collectSkillHealth({ now: new Date() }); // Date object
// after
health.collectSkillHealth({ now: new Date().toISOString() }); Defensive patterns
Strategy: validation
Validate before calling
function assertIsoNow(now) {
if (now !== undefined && (typeof now !== 'string' || Number.isNaN(Date.parse(now)))) {
throw new Error(`now must be an ISO date string, got: ${now}`);
}
} Type guard
const isIsoTimestamp = (v) => v === undefined || (typeof v === 'string' && !Number.isNaN(Date.parse(v)));
Try / catch
try {
report = health.collectSkillHealth(opts);
} catch (err) {
if (err.message.startsWith('Invalid now timestamp')) {
delete opts.now;
report = health.collectSkillHealth(opts);
} else throw err;
} Prevention
- Convert any Date objects to .toISOString() strings before passing as options.now.
- Validate timestamps in test fixtures with Date.parse in a beforeAll hook.
- Keep one canonical timestamp format across config, env, and code.
When it happens
Trigger: collectSkillHealth({ now: 'yesterday' }); passing a Date instance instead of a string; passing an empty string ('' parses to NaN via Date.parse); forwarding a raw CLI/env string like '2026-13-45' (invalid month/day) into options.now.
Common situations: Scheduled jobs reading a timestamp from config files with human-formatted dates; tests injecting fixture timestamps with the wrong format; mixing Date objects and strings across callers; locale-dependent date strings produced on a developer machine.
Related errors
- Invalid now timestamp
- Invalid warn threshold
- must be an ISO-8601 timestamp.
- recorded_at must be an ISO timestamp
- -32602
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/f6a08214d441b472.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/skill-evolution/health.js:149
if (records.length === 0) {
return null;
}
return records
.map(record => ({
timestamp: record.recorded_at,
timeMs: Date.parse(record.recorded_at),
}))
.filter(entry => !Number.isNaN(entry.timeMs))
.sort((left, right) => left.timeMs - right.timeMs)
.at(-1)?.timestamp || null;
}
function collectSkillHealth(options = {}) {
const now = options.now || new Date().toISOString();
const nowMs = Date.parse(now);
if (Number.isNaN(nowMs)) {
throw new Error(`Invalid now timestamp: ${now}`);
}
const warnThreshold = typeof options.warnThreshold === 'number'
? options.warnThreshold
: Number(options.warnThreshold || 0.1);
if (!Number.isFinite(warnThreshold) || warnThreshold < 0) {
throw new Error(`Invalid warn threshold: ${options.warnThreshold}`);
}
const records = tracker.readSkillExecutionRecords(options);
const skillsById = discoverSkills(options);
const recordsBySkill = records.reduce((groupedRecords, record) => {
if (!groupedRecords.has(record.skill_id)) {
groupedRecords.set(record.skill_id, []);
}
groupedRecords.get(record.skill_id).push(record);
return groupedRecords;View on GitHub (pinned to 8321021c54)