{"record":{"id":"9a541392c0545f22","repo":"mastra-ai/mastra","slug":"invalid-retention-duration-duration-must-be-a","errorCode":null,"errorMessage":"Invalid retention duration: ${duration}. Must be a non-negative finite number of milliseconds.","messagePattern":"Invalid retention duration: (.+?)\\. Must be a non-negative finite number of milliseconds\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/storage/utils.ts","lineNumber":64,"sourceCode":"  s: 1000,\n  m: 60 * 1000,\n  h: 60 * 60 * 1000,\n  d: 24 * 60 * 60 * 1000,\n  w: 7 * 24 * 60 * 60 * 1000,\n};\n\n/**\n * Parses a retention {@link Duration} into milliseconds.\n *\n * Accepts a raw number of milliseconds or a `<number><unit>` string where unit\n * is one of `ms`, `s`, `m`, `h`, `d`, `w`.\n *\n * @throws Error if the input is not a valid duration.\n */\nexport function parseDuration(duration: Duration): number {\n  if (typeof duration === 'number') {\n    if (!Number.isFinite(duration) || duration < 0) {\n      throw new Error(`Invalid retention duration: ${duration}. Must be a non-negative finite number of milliseconds.`);\n    }\n    return duration;\n  }\n\n  const match = /^(\\d+(?:\\.\\d+)?)(ms|s|m|h|d|w)$/.exec(duration);\n  if (!match) {\n    throw new Error(\n      `Invalid retention duration: \"${duration}\". Expected a number of milliseconds or a \"<number><unit>\" string (ms, s, m, h, d, w).`,\n    );\n  }\n\n  const value = Number(match[1]);\n  const unit = match[2]!;\n  return value * DURATION_UNIT_MS[unit]!;\n}\n\nexport function safelyParseJSON(input: any): any {\n  // If already an object (and not null), return as-is","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/storage/utils.ts#L46-L82","documentation":"parseDuration converts a Duration (number of milliseconds or a string like '30s'/'2h'/'7d') into milliseconds. For numeric input it requires a finite, non-negative value; anything else (NaN, Infinity, negative) throws this error. String inputs must match the unit regex or fail with a similar error.","triggerScenarios":"Passing a retention/TTL number that is negative, NaN, or Infinity to a storage/retention option that runs through parseDuration; reading the value from an env var with Number() yielding NaN.","commonSituations":"Number(process.env.RETENTION) when the env var is unset (NaN) or '-1'; computing a duration via division that produces Infinity; copying a value in the wrong unit (seconds instead of milliseconds) resulting in a negative after subtraction.","solutions":["Validate the numeric value before passing: Number.isFinite(v) && v >= 0.","Parse env vars safely: const v = Number(raw); if (!Number.isFinite(v) || v < 0) throw; then pass v.","Use a valid duration string ('30s', '1h', '7d') instead of an invalid number if the API accepts strings."],"exampleFix":"// before\nconst ttl = Number(process.env.RETENTION_MS); // NaN when unset\n// after\nconst parsed = Number(process.env.RETENTION_MS ?? '604800000');\nconst ttl = Number.isFinite(parsed) && parsed >= 0 ? parsed : 604800000;","handlingStrategy":"validation","validationCode":"function toValidMs(v: number | string): number | string {\n  if (typeof v === 'number' && (!Number.isFinite(v) || v < 0)) {\n    throw new Error(`Invalid duration: ${v}`);\n  }\n  return v;\n}","typeGuard":"function isValidDuration(d: unknown): d is number {\n  return typeof d === 'number' && Number.isFinite(d) && d >= 0\n    || (typeof d === 'string' && /^\\d+(?:\\.\\d+)?(ms|s|m|h|d|w)$/.test(d));\n}","tryCatchPattern":"try {\n  const ms = parseDuration(rawDuration);\n} catch (e) {\n  console.error('Bad retention config, falling back to 7d');\n  const ms = parseDuration('7d');\n}","preventionTips":["Guard env-derived numbers with Number.isFinite and >= 0 checks.","Prefer unit-suffixed strings ('30s', '1h') over raw numbers for readability.","Never compute durations with operations that can yield NaN/Infinity from unset inputs."],"tags":["validation","configuration","duration"],"backgroundTag":"invalid-duration","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}