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
- Break the cycle: extract the shared steps into a third workflow that both A and B nest, so dependencies go one way.
- Flatten the mutual recursion by inlining one direction's steps instead of calling back.
- Inspect the ids listed in the message and draw the nesting graph to find the exact loop, then remove one edge.
- 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
- Run a topological-sort check on nested-workflow references before bundling.
- Extract shared steps into a common child workflow instead of mutual nesting.
- Never nest a workflow inside itself or a descendant.
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
- @mastra/livekit: `workflowInput` is required when `workflow`
- Dynamic workflow bundle contains more than one definition wi
- SCHEDULES_INVALID_WORKFLOW_PATCH
- ${path} must contain only plain objects.
- Workflow definition graph must be an array.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/bb51dc23956fcfaa.
Report an issue: GitHub.