{"record":{"id":"ec00f20c62729350","repo":"mastra-ai/mastra","slug":"sleepuntil-step-entry-id-cannot-be-stored-dy","errorCode":null,"errorMessage":"SleepUntil step \"${entry.id}\" cannot be stored: dynamic date (function) is not supported.","messagePattern":"SleepUntil step \"(.+?)\" cannot be stored: dynamic date \\(function\\) is not supported\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/workflows/dynamic/serialize.ts","lineNumber":53,"sourceCode":"export function toStorableGraph(stepFlow: StepFlowEntry[]): SerializedStepFlowEntry[] {\n  return stepFlow.map(entry => serializeEntry(entry));\n}\n\nfunction serializeEntry(entry: StepFlowEntry): SerializedStepFlowEntry {\n  switch (entry.type) {\n    case 'step':\n    case 'agent':\n    case 'tool':\n    case 'mapping':\n      return serializeSingleEntry(entry);\n    case 'sleep':\n      if (typeof entry.duration !== 'number') {\n        throw new Error(`Sleep step \"${entry.id}\" cannot be stored: dynamic duration (function) is not supported.`);\n      }\n      return { type: 'sleep', id: entry.id, duration: entry.duration };\n    case 'sleepUntil':\n      if (!(entry.date instanceof Date)) {\n        throw new Error(`SleepUntil step \"${entry.id}\" cannot be stored: dynamic date (function) is not supported.`);\n      }\n      return { type: 'sleepUntil', id: entry.id, date: entry.date };\n    case 'parallel':\n      return { type: 'parallel', steps: entry.steps.map(s => serializeSingleEntry(s)) };\n    case 'foreach':\n      if (entry.step.type === 'mapping') {\n        throw new Error(\n          `Foreach step cannot iterate a mapping: mappings project data, they don't execute per item. Use an agent, tool, or plain step as the foreach body.`,\n        );\n      }\n      return {\n        type: 'foreach',\n        step: serializeSingleEntry(entry.step),\n        opts:\n          typeof entry.opts.concurrency === 'function'\n            ? { fn: entry.opts.concurrency.toString() }\n            : { concurrency: entry.opts.concurrency },\n      };","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/workflows/dynamic/serialize.ts#L35-L71","documentation":"When persisting a dynamic workflow via `toStorableGraph`, every step entry must be JSON-safe. A `sleepUntil` entry whose `date` is not a literal `Date` instance (typically a function computing the date at runtime, e.g. `() => new Date(Date.now() + 60000)`) cannot be serialized, because a closure would be lost on storage and rehydration. The library throws instead of silently dropping the dynamic behavior, which would produce a broken persisted workflow.","triggerScenarios":"Calling `toStorableGraph(stepFlow)` (directly or via workflow persistence/save APIs) while the `stepFlow` contains a `sleepUntil` entry created with a function-valued `date`, or a `date` that is a plain string/number rather than a `Date` instance.","commonSituations":"Authoring dynamic workflows with computed wake times: sleeping until a business-hours boundary, a deadline stored in request context, or 'now plus N seconds'. Developers naturally pass a function because the runtime loop supports it, then persistence fails.","solutions":["Replace the function form with a concrete `Date` instance (e.g. `new Date(Date.now() + 60000)`) computed before building the entry.","If the wake time must be computed at run time, keep the workflow non-persisted, or model the wait as a custom step that suspends and resumes at the computed date.","Validate the entry before persisting: `entry.type === 'sleepUntil' && entry.date instanceof Date`."],"exampleFix":"// before\nsleepUntil(() => new Date(Date.now() + 60_000))\n\n// after\nsleepUntil(new Date(Date.now() + 60_000))","handlingStrategy":"type-guard","validationCode":"function isStorableSleepUntil(e) { return e.type !== 'sleepUntil' || e.date instanceof Date; }\nstepFlow.forEach(e => { if (!isStorableSleepUntil(e)) throw new Error(`sleepUntil \"${e.id}\" needs a literal Date before persisting`); });","typeGuard":"const hasLiteralDate = (e: StepFlowEntry): e is StepFlowEntry & { type: 'sleepUntil'; date: Date } =>\n  e.type === 'sleepUntil' && e.date instanceof Date;","tryCatchPattern":"try {\n  storable = toStorableGraph(stepFlow);\n} catch (e) {\n  if (/SleepUntil step .* cannot be stored/.test(e.message)) {\n    // fall back to non-persisted run or fix the entry\n  } else throw e;\n}","preventionTips":["Always pass `new Date(...)` instances to sleepUntil when the workflow may be persisted.","Compute dynamic wake times in a host step, not as a closure in the entry.","Add a pre-save lint that walks stepFlow and rejects function-valued fields."],"tags":["workflows","serialization","closure"],"backgroundTag":"non-serializable-closure","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}