affaan-m/ECC · error · Error

Invalid now timestamp: ${now}

Error message

Invalid now timestamp: ${now}

What it means

Thrown by renderDashboard when options.now is provided but Date.parse(now) returns NaN. When omitted, now defaults to new Date().toISOString() which is always valid, so the error only fires on an explicit bad value.

Source

Thrown at scripts/lib/skill-evolution/dashboard.js:335

      for (const version of skill.versions) {
        const date = version.created_at ? version.created_at.slice(0, 10) : '-';
        const reason = version.reason || '-';
        lines.push(`  v${version.version} \u2500\u2500 ${date} \u2500\u2500 ${reason}`);
      }
    }
  }

  return {
    text: panelBox('Version History', lines, width),
    data: { skills: skillVersions },
  };
}

function renderDashboard(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 dashboardOptions = { ...options, now };
  const records = tracker.readSkillExecutionRecords(dashboardOptions);
  const skillsById = health.discoverSkills(dashboardOptions);
  const report = health.collectSkillHealth(dashboardOptions);
  const summary = health.summarizeHealthReport(report);

  const panelRenderers = {
    'success-rate': () => renderSuccessRatePanel(records, report.skills, dashboardOptions),
    'failures': () => renderFailureClusterPanel(records, dashboardOptions),
    'amendments': () => renderAmendmentPanel(skillsById, dashboardOptions),
    'versions': () => renderVersionTimelinePanel(skillsById, dashboardOptions),
  };

  const selectedPanel = options.panel || null;
  if (selectedPanel && !VALID_PANELS.has(selectedPanel)) {
    throw new Error(`Unknown panel: ${selectedPanel}. Valid panels: ${Array.from(VALID_PANELS).join(', ')}`);

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Omit options.now to use the current time, or pass a strict ISO 8601 string (new Date().toISOString()).
  2. Validate with Number.isNaN(Date.parse(now)) before passing.
  3. If accepting user input, parse and re-serialize: new Date(input).toISOString().

Example fix

// before
renderDashboard({ now: userInput }); // userInput = '2025-31-12'

// after
renderDashboard({ now: new Date(userInput).toISOString() });
Defensive patterns

Strategy: validation

Validate before calling

function safeNow(now) {
  const v = now || new Date().toISOString();
  if (Number.isNaN(Date.parse(v))) throw new TypeError('now is not a date');
  return v;
}
renderDashboard({ now: safeNow(opts.now) });

Type guard

function isIsoTimestamp(v) {
  return typeof v === 'string' && !Number.isNaN(Date.parse(v));
}

Try / catch

try {
  renderDashboard({ now });
} catch (err) {
  if (/Invalid now timestamp/.test(err.message)) renderDashboard({}); // drop bad now
  else throw err;
}

Prevention

When it happens

Trigger: Calling renderDashboard({ now: 'yesterday' }), renderDashboard({ now: '12/31/25' }) with a locale format some engines reject, or renderDashboard({ now: someUndefinedVar }) where the var is a non-empty garbage string. Empty string is falsy and falls back to the default, so it does not trigger.

Common situations: Forwarding a user-typed date string without validation; passing a Date object instead of its ISO string (Date.parse(dateObj) works, but inconsistent across engines); timezone/offset formatting that produces an unparseable token.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/4ac15f43d6f9f2aa. Report an issue: GitHub.