ruvnet/ruflo · error · Error
${label} must be an ISO timestamp
Error message
${label} must be an ISO timestamp What it means
In InMemoryRunReceiptReference.recordRun, the timestamp() helper validates run.startedAt and run.completedAt with Date.parse and throws `${label} must be an ISO timestamp` when the result is not finite. The labels embedded in the message are 'startedAt' and 'completedAt'. Receipt ordering and identity depend on these values, so only parseable ISO 8601 strings are accepted.
Source
Thrown at v3/@claude-flow/codex/src/harness/in-memory-run-receipt-reference.ts:17
import { createHash } from 'node:crypto';
import type { RunEvidence, RunReceipt } from './contract.js';
import { canonicalJson } from './repository-state.js';
const DIGEST = /^sha256:[0-9a-f]{64}$/;
function sha256(value: string): string {
return `sha256:${createHash('sha256').update(value).digest('hex')}`;
}
function copy<T>(value: T): T {
return structuredClone(value);
}
function timestamp(value: string, label: string): number {
const parsed = Date.parse(value);
if (!Number.isFinite(parsed)) throw new Error(`${label} must be an ISO timestamp`);
return parsed;
}
function validateRun(run: RunEvidence): void {
if (!run.executionId.trim() || !run.sessionId.trim() || !run.workloadId.trim()) {
throw new Error('run execution, session, and workload identity are required');
}
if (!DIGEST.test(run.sourceState.sourceStateId)) throw new Error('run sourceStateId is invalid');
if (!DIGEST.test(run.commandDigest) || !DIGEST.test(run.evidenceDigest)) {
throw new Error('run command and evidence digests must be canonical sha256 values');
}
if (!Number.isSafeInteger(run.exitCode)) throw new Error('run exitCode must be a safe integer');
const started = timestamp(run.startedAt, 'startedAt');
const completed = timestamp(run.completedAt, 'completedAt');
if (completed < started) throw new Error('run completedAt precedes startedAt');
if (
run.buildEvidence !== undefined
&& run.buildEvidence.sourceStateId !== run.sourceState.sourceStateIdView on GitHub (pinned to fa13ee4ad6)
Solutions
- Generate both timestamps with new Date().toISOString() at capture time
- Pre-check Number.isFinite(Date.parse(value)) for both fields before recordRun
- When integrating epoch-millisecond sources, convert first: new Date(ms).toISOString()
Example fix
// before
receipt.recordRun({ ..., startedAt: String(Date.now() - 60000), completedAt: String(Date.now()) });
// after
receipt.recordRun({ ..., startedAt: new Date(Date.now() - 60000).toISOString(), completedAt: new Date().toISOString() }); Defensive patterns
Strategy: validation
Validate before calling
function assertIsoTimestamp(value: string, label: string): void {
if (!Number.isFinite(Date.parse(value))) {
throw new TypeError(`${label} is not an ISO timestamp: ${JSON.stringify(value)}`);
}
}
assertIsoTimestamp(run.startedAt, 'startedAt');
assertIsoTimestamp(run.completedAt, 'completedAt'); Type guard
function isIsoTimestamp(value: unknown): value is string {
return typeof value === 'string' && Number.isFinite(Date.parse(value));
} Try / catch
try {
ledger.recordRun(run);
} catch (error) {
if (error instanceof Error && error.message.endsWith('must be an ISO timestamp')) {
// re-stamp both timestamps from one clock and retry once
run.startedAt = new Date(startedMs).toISOString();
run.completedAt = new Date(completedMs).toISOString();
ledger.recordRun(run);
} else throw error;
} Prevention
- Generate all timestamps with new Date().toISOString() at capture time
- Never pass Date objects, Date.now() numbers, or locale-formatted strings into RunEvidence
- Validate both timestamp fields at the boundary where RunEvidence is constructed
When it happens
Trigger: recordRun with startedAt: 'yesterday', completedAt: '12/31/2026 10:00', '', '2026-31-12T00:00:00Z', a locale-formatted log timestamp, a raw epoch number (1767225600000 instead of an ISO string), or a Date object serialized by a non-ISO toString path.
Common situations: Reusing timestamps captured by other tooling (MM/DD/YYYY or 'Jan 5, 2026' forms); passing Date.now() output or Date objects directly; strings assembled from log lines whose format drifts across locales/timezones.
Related errors
- run completedAt precedes startedAt
- Invalid timestamp format: "${value}". Expected ISO 8601.
- run execution, session, and workload identity are required
- run sourceStateId is invalid
- run command and evidence digests must be canonical sha256 va
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/72845cc1a8e5e5c3.
Report an issue: GitHub.