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
- 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`.
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
- 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.
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
- Conditional (branch) step cannot be stored: closure predicat
- Loop step "${getSingleStepEntryId(entry.step)}" cannot be st
- Mapping step "${entry.id}" cannot be stored: the function fo
- Mapping step "${entry.id}" key "${key}" cannot be stored: so
- ${kind === 'agent' ? 'Agent' : 'Tool'} step "${entryId}" can
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/ec00f20c62729350.
Report an issue: GitHub.