{"record":{"id":"804128d88752dd7a","repo":"mem0ai/mem0","slug":"expirationdate-must-be-a-valid-date-in-yyyy-mm-dd","errorCode":null,"errorMessage":"expirationDate must be a valid date in YYYY-MM-DD format.","messagePattern":"expirationDate must be a valid date in YYYY-MM-DD format\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mem0-ts/src/oss/src/utils/expiration.ts","lineNumber":36,"sourceCode":" * Python SDK rejects (\"12/31/2099\", \"2099\") and resolves them against the\n * local timezone, shifting the calendar day. It also silently rolls invalid\n * dates over — `new Date(\"2099-02-30T00:00:00Z\")` yields March 2nd.\n */\nexport function normalizeExpirationDate(value: string): string {\n  const match = EXPIRATION_DATE_PATTERN.exec(value);\n  if (match) {\n    const [, year, month, day] = match;\n    const parsed = new Date(`${value}T00:00:00Z`);\n    if (\n      !Number.isNaN(parsed.getTime()) &&\n      parsed.getUTCFullYear() === Number(year) &&\n      parsed.getUTCMonth() === Number(month) - 1 &&\n      parsed.getUTCDate() === Number(day)\n    ) {\n      return value;\n    }\n  }\n  throw new Error(\"expirationDate must be a valid date in YYYY-MM-DD format.\");\n}\n\n/** True when the payload carries an expiration date strictly before today (UTC). */\nexport function payloadIsExpired(\n  payload: Record<string, any> | null | undefined,\n) {\n  const raw = payload?.expiration_date;\n  if (!raw) return false;\n  try {\n    // YYYY-MM-DD sorts lexicographically the same way it sorts chronologically.\n    return normalizeExpirationDate(String(raw)) < todayUtc();\n  } catch {\n    // Unparseable stored value: treat as non-expiring rather than hiding data.\n    return false;\n  }\n}\n","sourceCodeStart":18,"sourceCodeEnd":53,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0-ts/src/oss/src/utils/expiration.ts#L18-L53","documentation":"Thrown by normalizeExpirationDate in the shared expiration utils when a value does not match YYYY-MM-DD or matches syntactically but is not a real calendar date. The regex match is re-validated by constructing a UTC Date and confirming the year, month, and day round-trip exactly. Expiration dates are stored as YYYY-MM-DD strings precisely so they sort lexicographically like dates.","triggerScenarios":"Passing expirationDate as '2024-13-01' (month 13), '2024-02-30' (nonexistent day), '2024/02/29' (wrong separators), '2024-1-5' (non-padded), or a full ISO timestamp like '2024-12-31T00:00:00Z'. Reachable via add/update config paths that normalize expiration dates, and indirectly by payloadIsExpired when stored payload values are malformed.","commonSituations":"Feeding new Date().toISOString() output (contains time and Z); user input from a date picker with a different format; locale-formatted dates; assuming partial dates like '2024-12' are accepted.","solutions":["Format as YYYY-MM-DD: date.toISOString().slice(0, 10)","Validate user-supplied dates before the call with the same round-trip check","If the error comes from payloadIsExpired on stored data, fix the corrupted expiration_date values in the vector store payload"],"exampleFix":"// before\nawait memory.add('note', { userId: 'u1', expirationDate: new Date('2026-12-31').toISOString() });\n\n// after\nawait memory.add('note', { userId: 'u1', expirationDate: '2026-12-31' });\n// or: expirationDate: new Date('2026-12-31').toISOString().slice(0, 10)","handlingStrategy":"validation","validationCode":"function toExpirationDate(d: Date | string): string {\n  const s = typeof d === 'string' ? d : d.toISOString().slice(0, 10);\n  const m = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(s);\n  if (!m) throw new Error(`invalid expirationDate: ${s}`);\n  const [y, mo, da] = m.slice(1).map(Number);\n  const t = new Date(Date.UTC(y, mo - 1, da));\n  if (t.getUTCFullYear() !== y || t.getUTCMonth() !== mo - 1 || t.getUTCDate() !== da) {\n    throw new Error(`not a real date: ${s}`);\n  }\n  return s;\n}","typeGuard":"const isYmdDate = (s: string): boolean =>\n  /^\\d{4}-\\d{2}-\\d{2}$/.test(s) && !Number.isNaN(new Date(`${s}T00:00:00Z`).getTime());","tryCatchPattern":null,"preventionTips":["Always emit YYYY-MM-DD via toISOString().slice(0, 10) — never pass full ISO timestamps","Normalize user date input at the boundary with a shared formatter before it reaches add()/update()"],"tags":["validation","expiration","date-format","oss"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}