mastra-ai/mastra · error · Error

Sleep step "${entry.id}" cannot be stored: dynamic duration

Error message

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

What it means

During serialization (`toStorableGraph`), a 'sleep' entry whose duration is not a plain number — i.e. a function computing the duration at runtime — cannot be persisted, because only static durations round-trip through the stored graph format. The library throws at serialize time so dynamic sleeps are never silently converted to a wrong static value.

Source

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

/**
 * Walk a live `stepFlow` and emit a JSON-safe `SerializedStepFlowEntry[]` with
 * full (un-truncated) mapping configs and all step/agent/tool references stored
 * as ids. Throws on entries that can't round-trip (closures, closure predicates).
 */
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),

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Replace the function duration with a fixed numeric duration if the value is known, then serialize.
  2. If the duration must be dynamic, keep that workflow code-defined instead of storing/rehydrating it.
  3. Move dynamic delay logic into a step that awaits a timer internally, with a static sleep in the stored graph.
  4. Pre-check before serializing: reject sleep entries whose duration is not a number and choose a static value.

Example fix

// before
wf.sleep(ctx => jitter(1000, 5000)); // cannot store

// after
wf.sleep(3000); // static duration round-trips
Defensive patterns

Strategy: validation

Validate before calling

function assertStorableSleeps(wf) {
  for (const e of wf.getStepGraph?.() ?? []) {
    if (e.type === 'sleep' && typeof e.duration !== 'number') {
      throw new Error(`Sleep step "${e.id}" uses a dynamic duration; replace with a number before storing`);
    }
    if (e.type === 'sleepUntil' && !(e.date instanceof Date)) {
      throw new Error(`sleepUntil "${e.id}" uses a dynamic date; replace with a Date before storing`);
    }
  }
}

Type guard

function isStaticSleep(e: { type: string; duration?: unknown }): e is { type: 'sleep'; duration: number } {
  return e.type === 'sleep' && typeof e.duration === 'number';
}

Try / catch

try {
  const stored = toStorableGraph(wf);
} catch (err) {
  if (err instanceof Error && err.message.includes('dynamic duration (function) is not supported')) {
    // rebuild workflow with a static sleep duration before persisting
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling the store/serialize path (toStorableGraph -> serializeEntry) on a dynamic workflow containing `.sleep(durationFn)` where `typeof entry.duration !== 'number'` — a function or other non-number was passed as the sleep duration.

Common situations: Using `.sleep(ctx => computedMs)` for backoff/jitter and then attempting to persist the workflow; storing workflows built with runtime-computed sleeps for cross-service deployment.

Related errors


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