{"record":{"id":"d23ae71244204e7e","repo":"affaan-m/ECC","slug":"label-must-be-an-iso-8601-timestamp","errorCode":null,"errorMessage":"${label} must be an ISO-8601 timestamp.","messagePattern":"(.+?) must be an ISO-8601 timestamp\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/memory-vault-format.js","lineNumber":139,"sourceCode":"  }\n  return values.reduce((result, value) => {\n    const normalized = validator(value);\n    if (result.includes(normalized)) {\n      throw new Error(`${label} must not contain duplicate values.`);\n    }\n    return [...result, normalized];\n  }, []);\n}\n\nfunction validateTimestamp(value, label) {\n  const normalized = asNonEmptyString(value, label, 64);\n  const parsed = new Date(normalized);\n  if (\n    !ISO_TIMESTAMP_PATTERN.test(normalized)\n    || Number.isNaN(parsed.getTime())\n    || parsed.toISOString() !== normalized\n  ) {\n    throw new Error(`${label} must be an ISO-8601 timestamp.`);\n  }\n  return normalized;\n}\n\nfunction normalizeBody(value) {\n  if (typeof value !== 'string') {\n    throw new Error('memory body must be a string.');\n  }\n  if (hasUnsafeControlCharacters(value, true)) {\n    throw new Error('memory body must not contain unsafe control or bidirectional formatting characters.');\n  }\n  const normalized = value.trim();\n  if (normalized.length === 0) {\n    throw new Error('memory body must contain non-whitespace context.');\n  }\n  if (Buffer.byteLength(normalized, 'utf8') > MAX_BODY_BYTES) {\n    throw new Error(`memory body is too large (maximum ${MAX_BODY_BYTES} bytes).`);\n  }","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/memory-vault-format.js#L121-L157","documentation":"validateTimestamp requires an ISO-8601 UTC timestamp in the exact form YYYY-MM-DDTHH:MM:SS.sssZ (regex + Date parse + round-trip through toISOString must all agree). The library throws this so every created_at/updated_at field is a canonical, lexicographically sortable UTC instant.","triggerScenarios":"Calling normalizeMemory with created_at/updated_at values like '2026-09-16 10:00:00' (space, no T/Z), '2026-09-16T10:00:00Z' (missing milliseconds), a local-offset timestamp '2026-09-16T10:00:00+02:00', or a non-parseable string.","commonSituations":"Hand-written frontmatter using human-readable dates; new Date().toString() output pasted directly; second-precision timestamps from logs; timestamps generated in a non-UTC timezone; JSON dates serialized by a library that omits milliseconds.","solutions":["Generate timestamps with new Date().toISOString(), which always produces the required YYYY-MM-DDTHH:MM:SS.sssZ form.","If you have a Date/string in another format, convert: new Date(value).toISOString() and verify it round-trips.","Append the missing '.000' milliseconds and use 'Z' (UTC) instead of a numeric offset or space separator.","Confirm the value is a string, not a Date object or epoch number."],"exampleFix":"// before\ncreated_at: '2026-09-16 12:00:00'\n// after\ncreated_at: new Date().toISOString() // '2026-09-16T12:00:00.000Z'","handlingStrategy":"validation","validationCode":"const iso = (v) => new Date(v).toISOString(); // always YYYY-MM-DDTHH:MM:SS.sssZ\nmem.createdAt = iso(mem.createdAt ?? Date.now());","typeGuard":"const isIsoTimestamp = (v) => typeof v === 'string' && /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$/.test(v) && !Number.isNaN(Date.parse(v));","tryCatchPattern":"try { normalizeMemory(mem); } catch (e) { if (e.message.includes('ISO-8601 timestamp')) { mem.updatedAt = new Date(mem.updatedAt).toISOString(); } else throw e; }","preventionTips":["Always generate timestamps with new Date().toISOString()","Never hand-write timestamps in frontmatter","Convert local/offset timestamps to UTC before storing","Add an isIsoTimestamp assertion in tests for all date fields"],"tags":["validation","timestamp","iso8601","memory-vault"],"backgroundTag":"invalid-date-format","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}