mastra-ai/mastra · error · Error

SleepUntil step "${entry.id}" cannot be stored: dynamic date

Error message

SleepUntil step "${entry.id}" cannot be stored: dynamic date (function) is not supported.

What it means

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.

Source

Thrown at packages/core/src/workflows/dynamic/serialize.ts:53

export function toStorableGraph(stepFlow: StepFlowEntry[]): SerializedStepFlowEntry[] {
  return stepFlow.map(entry => serializeEntry(entry));
}

function serializeEntry(entry: StepFlowEntry): SerializedStepFlowEntry {
  switch (entry.type) {
    case 'step':
    case 'agent':
    case 'tool':
    case 'mapping':
      return serializeSingleEntry(entry);
    case 'sleep':
      if (typeof entry.duration !== 'number') {
        throw new Error(`Sleep step "${entry.id}" cannot be stored: dynamic duration (function) is not supported.`);
      }
      return { type: 'sleep', id: entry.id, duration: entry.duration };
    case 'sleepUntil':
      if (!(entry.date instanceof Date)) {
        throw new Error(`SleepUntil step "${entry.id}" cannot be stored: dynamic date (function) is not supported.`);
      }
      return { type: 'sleepUntil', id: entry.id, date: entry.date };
    case 'parallel':
      return { type: 'parallel', steps: entry.steps.map(s => serializeSingleEntry(s)) };
    case 'foreach':
      if (entry.step.type === 'mapping') {
        throw new Error(
          `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.`,
        );
      }
      return {
        type: 'foreach',
        step: serializeSingleEntry(entry.step),
        opts:
          typeof entry.opts.concurrency === 'function'
            ? { fn: entry.opts.concurrency.toString() }
            : { concurrency: entry.opts.concurrency },
      };

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Replace the function form with a concrete `Date` instance (e.g. `new Date(Date.now() + 60000)`) computed before building the entry.
  2. 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.
  3. Validate the entry before persisting: `entry.type === 'sleepUntil' && entry.date instanceof Date`.

Example fix

// before
sleepUntil(() => new Date(Date.now() + 60_000))

// after
sleepUntil(new Date(Date.now() + 60_000))
Defensive patterns

Strategy: type-guard

Validate before calling

function isStorableSleepUntil(e) { return e.type !== 'sleepUntil' || e.date instanceof Date; }
stepFlow.forEach(e => { if (!isStorableSleepUntil(e)) throw new Error(`sleepUntil "${e.id}" needs a literal Date before persisting`); });

Type guard

const hasLiteralDate = (e: StepFlowEntry): e is StepFlowEntry & { type: 'sleepUntil'; date: Date } =>
  e.type === 'sleepUntil' && e.date instanceof Date;

Try / catch

try {
  storable = toStorableGraph(stepFlow);
} catch (e) {
  if (/SleepUntil step .* cannot be stored/.test(e.message)) {
    // fall back to non-persisted run or fix the entry
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/ec00f20c62729350. Report an issue: GitHub.