mastra-ai/mastra · error · Error

Dynamic workflow bundle has a circular nested-workflow depen

Error message

Dynamic workflow bundle has a circular nested-workflow dependency among: ${Array.from(remaining.keys()).sort().join(', ')}.

What it means

Dynamic workflow bundles are hydrated in topological order: each pass registers members whose nested workflows are already hydrated. If a full pass registers nothing yet members remain, their nested-workflow references form a cycle that can never resolve, and Mastra throws this plain Error listing the ids still unresolved.

Source

Thrown at packages/core/src/mastra/index.ts:5031

    const ordered: typeof members = [];
    const remaining = new Map(members.map(member => [member.normalized.id, member] as const));
    const hydrated = new Set<string>();
    let progress = true;
    while (remaining.size > 0 && progress) {
      progress = false;
      for (const [id, member] of Array.from(remaining)) {
        const pending = Array.from(collectNestedWorkflowIds(member.normalized.graph)).filter(
          dependency => dependency !== id && bundleIds.has(dependency) && !hydrated.has(dependency),
        );
        if (pending.length > 0) continue;
        remaining.delete(id);
        hydrated.add(id);
        ordered.push(member);
        progress = true;
      }
    }
    if (remaining.size > 0) {
      throw new Error(
        `Dynamic workflow bundle has a circular nested-workflow dependency among: ${Array.from(remaining.keys())
          .sort()
          .join(', ')}.`,
      );
    }

    // Snapshot the registry slots this bundle will overwrite so a failure
    // anywhere below leaves the instance exactly as it was found.
    const registry = this.#workflows as Record<string, AnyWorkflow>;
    const priorWorkflows = new Map<string, AnyWorkflow | undefined>();
    const priorHiddenKeys = new Set<string>();
    for (const { normalized } of ordered) {
      priorWorkflows.set(normalized.id, registry[normalized.id]);
      if (this.#hiddenWorkflowKeys.has(normalized.id)) priorHiddenKeys.add(normalized.id);
    }
    const restoreRegistry = () => {
      for (const [id, prior] of priorWorkflows) {
        if (prior) registry[id] = prior;

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Break the cycle: extract the shared steps into a third workflow that both A and B nest, so dependencies go one way.
  2. Flatten the mutual recursion by inlining one direction's steps instead of calling back.
  3. Inspect the ids listed in the message and draw the nesting graph to find the exact loop, then remove one edge.
  4. If the recursion is intentional, restructure into an iterative loop inside a single workflow rather than nested workflow references.

Example fix

// before
defs = [
  { id: 'order', nested: ['payment'] },
  { id: 'payment', nested: ['order'] }, // cycle
];
// after
defs = [
  { id: 'validate', nested: [] },
  { id: 'order', nested: ['validate', 'payment'] },
  { id: 'payment', nested: ['validate'] },
];
Defensive patterns

Strategy: validation

Validate before calling

function assertAcyclic(defs) {
  const deps = new Map(defs.map(d => [d.id, d.nested ?? []]));
  const state = new Map();
  const visit = id => {
    if (state.get(id) === 'visiting') throw new Error(`Cycle involving workflow "${id}"`);
    if (state.get(id) === 'done') return;
    state.set(id, 'visiting');
    for (const n of deps.get(id) ?? []) visit(n);
    state.set(id, 'done');
  };
  for (const id of deps.keys()) visit(id);
}

Try / catch

try {
  await registerDynamicWorkflowBundle(defs);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Dynamic workflow bundle has a circular nested-workflow dependency')) {
    console.error('Fix the nesting cycle among:', e.message);
  } else throw e;
}

Prevention

When it happens

Trigger: Registering a bundle where workflow A nests workflow B and B nests (or references) A, directly or through a longer chain — e.g. A -> B -> C -> A — so no member's dependencies are ever satisfied.

Common situations: Two team workflows calling each other for shared steps; a refactor turning a one-way call into a mutual one; copy/paste wiring the parent id into a child by mistake; self-nesting a workflow inside itself.

Related errors


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