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

  1. Rebuild the loop with the declarative predicate API (serializable condition object) and re-serialize the workflow.
  2. Verify the stored loop entry contains both `predicate` and `loopType: 'dowhile' | 'dountil'`.
  3. Re-export stored graphs with the current serializer if they were produced by an older version.
  4. 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

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


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