{"record":{"id":"defc12a0b1af7fc4","repo":"mastra-ai/mastra","slug":"invalid-retention-duration-duration-expecte","errorCode":null,"errorMessage":"Invalid retention duration: \"${duration}\". Expected a number of milliseconds or a \"<number><unit>\" string (ms, s, m, h, d, w).","messagePattern":"Invalid retention duration: \"(.+?)\"\\. Expected a number of milliseconds or a \"<number><unit>\" string \\(ms, s, m, h, d, w\\)\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/storage/utils.ts","lineNumber":71,"sourceCode":"/**\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\n  if (input && typeof input === 'object') return input;\n  if (input == null) return {};\n  // If it's a string, try to parse\n  if (typeof input === 'string') {\n    try {\n      return JSON.parse(input);\n    } catch {","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/storage/utils.ts#L53-L89","documentation":"parseDuration validates retention duration values for storage (e.g. per-page retention on traces/workflows). It accepts either a number of milliseconds or a string like '30s', '2h', '7d'. If the string doesn't match the pattern ^\\d+(?:\\.\\d+)?(ms|s|m|h|d|w)$, this error is thrown at parse time rather than silently storing an unusable retention config.","triggerScenarios":"Passing a string with an unsupported unit (e.g. '30min', '1day', '24H', '1y'), a bare unitless string ('30'), extra whitespace (' 30s'), a negative value ('-5s'), or a non-numeric string ('weekly') to a retention option.","commonSituations":"Copy-pasting durations from docs of other libraries that use different unit vocabularies ('min', 'day', 'months'); building durations via template strings like `${n}${'mins'}`; TypeScript typing a config as string and slipping through an invalid value at runtime.","solutions":["Use one of the supported units exactly: ms, s, m, h, d, w (e.g. '90s', '30m', '7d')","Pass a plain number of milliseconds instead of a string (e.g. 30000 instead of '30s')","Trim the string and lowercase it before passing (e.g. ' 30s '.trim().toLowerCase())","Convert unsupported units yourself: '30min' → '30m', '1day' → '1d'"],"exampleFix":"// before\nnew Mastra({ storage, config: { workflows: { retention: { workflorRuns: '30days' } } } });\n// after\nnew Mastra({ storage, config: { workflows: { retention: { workflowRuns: '30d' } } } });","handlingStrategy":"validation","validationCode":"function isValidRetentionDuration(d: unknown): boolean {\n  if (typeof d === 'number') return Number.isFinite(d) && d >= 0;\n  return typeof d === 'string' && /^\\d+(?:\\.\\d+)?(ms|s|m|h|d|w)$/.test(d.trim());\n}\nif (!isValidRetentionDuration(duration)) throw new Error(`Bad duration: ${duration}`);","typeGuard":"function isDurationString(v: unknown): v is `${number}${'ms'|'s'|'m'|'h'|'d'|'w'}` {\n  return typeof v === 'string' && /^\\d+(?:\\.\\d+)?(ms|s|m|h|d|w)$/.test(v);\n}","tryCatchPattern":"try {\n  config.retention = parseDuration(userInput);\n} catch (e) {\n  logger.error('Invalid retention duration', { input: userInput, error: e });\n  config.retention = 7 * 24 * 60 * 60 * 1000; // safe default\n}","preventionTips":["Use a small helper/constants for durations instead of raw strings","Prefer plain millisecond numbers in programmatic config; reserve strings for human-written config","Add a startup validation pass that runs parseDuration on all configured durations","Never build duration strings via templates with free-form unit variables"],"tags":["validation","configuration","storage"],"backgroundTag":"invalid-duration-format","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}