affaan-m/ECC · error
recorded_at must be an ISO timestamp
Error message
recorded_at must be an ISO timestamp
What it means
Thrown by normalizeExecutionRecord when recorded_at fails Date.parse. The value is resolved as input.recorded_at, else options.now, else new Date().toISOString() (always valid). So this only fires when an explicit recorded_at (or options.now) is unparseable.
Source
Thrown at scripts/lib/skill-evolution/tracker.js:78
if (typeof skillVersion !== 'string' || skillVersion.trim().length === 0) {
throw new Error('skill_version is required');
}
if (typeof taskDescription !== 'string' || taskDescription.trim().length === 0) {
throw new Error('task_description is required');
}
if (!VALID_OUTCOMES.has(outcome)) {
throw new Error('outcome must be one of success, failure, or partial');
}
if (userFeedback !== null && !VALID_FEEDBACK.has(userFeedback)) {
throw new Error('user_feedback must be accepted, corrected, rejected, or null');
}
if (Number.isNaN(Date.parse(recordedAt))) {
throw new Error('recorded_at must be an ISO timestamp');
}
return {
skill_id: skillId,
skill_version: skillVersion,
task_description: taskDescription,
outcome,
failure_reason: input.failure_reason || input.failureReason || null,
tokens_used: toNullableNumber(input.tokens_used ?? input.tokensUsed, 'tokens_used'),
duration_ms: toNullableNumber(input.duration_ms ?? input.durationMs, 'duration_ms'),
user_feedback: userFeedback,
recorded_at: recordedAt,
};
}
function readJsonl(filePath) {
if (!fs.existsSync(filePath)) {
return [];View on GitHub (pinned to 01e15490f0)
Solutions
- Pass recorded_at as an ISO 8601 string, or omit it to use options.now / the current time.
- Pre-validate with Number.isNaN(Date.parse(value)).
- Normalize via new Date(value).toISOString().
Example fix
// before
recordSkillRun({ ..., recorded_at: '12/31/2025 5pm' });
// after
recordSkillRun({ ..., recorded_at: new Date('12/31/2025 5pm').toISOString() }); Defensive patterns
Strategy: validation
Validate before calling
function resolveRecordedAt(input, opts) {
const v = input.recorded_at || opts.now || new Date().toISOString();
if (Number.isNaN(Date.parse(v))) throw new TypeError('recorded_at not ISO');
return v;
} Type guard
function isIsoTimestamp(v) {
return typeof v === 'string' && !Number.isNaN(Date.parse(v));
} Try / catch
try {
recordSkillRun(input);
} catch (err) {
if (/recorded_at must be an ISO timestamp/.test(err.message)) { input.recorded_at = new Date().toISOString(); recordSkillRun(input); }
else throw err;
} Prevention
- Generate recorded_at with new Date().toISOString() at capture time.
- Let the recorder default it rather than passing a value.
- Validate timestamps at ingest for replayed/imported data.
When it happens
Trigger: recordSkillRun({ ..., recorded_at: 'today' }); recorded_at: '2025/12/31' in a format the engine rejects; options.now set to a bad string and no input.recorded_at to override it.
Common situations: Forwarding a locale date; passing a unix number where a string is expected (Date.parse(number) is engine-dependent); timezone tokens that break parsing.
Related errors
- ${label} must be an ISO-8601 timestamp.
- Invalid now timestamp: ${now}
- Invalid now timestamp: ${now}
- ${fieldName} must be a number
- skill execution payload must be an object
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/0aa5e8c4f590c350.
Report an issue: GitHub.