mastra-ai/mastra · error · Error
Cannot rehydrate loop step: missing declarative predicate or
Error message
Cannot rehydrate loop step: missing declarative predicate or loopType. Only declarative predicate loops round-trip.
What it means
A stored 'loop' entry can only be rehydrated if it has a declarative (serializable) `predicate` and an explicit `loopType` of 'dowhile' or 'dountil'. Loops whose predicate is a runtime function cannot be serialized, so the library refuses to reconstruct them rather than guessing loop semantics and silently changing behavior.
Source
Thrown at packages/core/src/workflows/dynamic/rehydrate.ts:183
// Wire graphs may omit the Studio-facing condition labels; derive them
// from the predicates (same convention as the fluent builder).
const serializedConditions =
entry.serializedConditions ??
steps.map((s, i) => ({ id: `${getSingleStepEntryId(s)}-condition`, fn: derivePredicateLabel(predicates[i]!) }));
const live: StepFlowEntry = {
type: 'conditional',
steps,
conditions: predicates.map(p => predicateToCondition(p!)),
serializedConditions,
predicates,
};
wf.__pushStepFlowEntry(live, { ...entry, serializedConditions });
return;
}
case 'loop': {
const { predicate, loopType } = entry;
if (!predicate || (loopType !== 'dowhile' && loopType !== 'dountil')) {
throw new Error(
`Cannot rehydrate loop step: missing declarative predicate or loopType. Only declarative predicate loops round-trip.`,
);
}
const step = rehydrateSingleEntry(entry.step, mastra, schemaOpts);
const serializedCondition = entry.serializedCondition ?? {
id: `${getSingleStepEntryId(step)}-condition`,
fn: derivePredicateLabel(predicate),
};
const live: StepFlowEntry = {
type: 'loop',
step,
condition: predicateToCondition(predicate),
loopType,
serializedCondition,
predicate,
};
wf.__pushStepFlowEntry(live, { ...entry, serializedCondition });
return;View on GitHub (pinned to 75dd419e61)
Solutions
- Rebuild the loop with the declarative predicate API (serializable condition object) and re-serialize the workflow.
- Verify the stored loop entry contains both `predicate` and `loopType: 'dowhile' | 'dountil'`.
- Re-export stored graphs with the current serializer if they were produced by an older version.
- Keep function-predicate loops as code-defined workflows instead of stored/rehydrated dynamic workflows.
Example fix
// stored entry (broken)
{ type: 'loop', step: loopStep } // no predicate/loopType
// fixed stored entry
{ type: 'loop', step: loopStep, loopType: 'dowhile', predicate: { id: 'check', ... } } Defensive patterns
Strategy: validation
Validate before calling
function hasRoundTrippableLoop(entry) {
return entry.type !== 'loop' ||
(Boolean(entry.predicate) && (entry.loopType === 'dowhile' || entry.loopType === 'dountil'));
} Type guard
function isRehydratableLoop(e: unknown): e is { type: 'loop'; predicate: unknown; loopType: 'dowhile' | 'dountil'; step: unknown } {
const x = e as any;
return !!x && x.type === 'loop' && !!x.predicate && (x.loopType === 'dowhile' || x.loopType === 'dountil');
} Try / catch
try {
const wf = rehydrateWorkflow(stored, mastra);
} catch (err) {
if (err instanceof Error && err.message.includes('Cannot rehydrate loop step')) {
// rebuild loop with declarative predicate from source
}
throw err;
} Prevention
- Use declarative loop predicates (serializable condition objects) with .dowhile()/.dountil().
- Never hand-edit stored loop entries; regenerate them via serialization.
- Test store -> rehydrate round-trips in CI to catch non-declarative predicates early.
When it happens
Trigger: rehydrateWorkflow encounters a stored graph entry with type 'loop' where entry.predicate is falsy, or entry.loopType is anything other than 'dowhile'/'dountil' (e.g. undefined because the original loop used a function predicate that failed to serialize).
Common situations: Persisting workflows built with `.dowhile(...)`/`.dountil(...)` using inline function predicates, then rehydrating them on another server or after restart; older stored graphs saved before declarative loop predicates were supported; hand-edited stored JSON dropping the loopType field.
Related errors
- Cannot rehydrate conditional step: missing or mismatched pre
- Unknown stored step type: ${JSON.stringify(_exhaustive)}
- Dynamic workflow references agent "${entry.agentId}" which i
- Dynamic workflow references tool "${entry.toolId}" which is
- Dynamic workflow references step "${id}" which is not regist
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/4377f50b35e78587.
Report an issue: GitHub.