mastra-ai/mastra · error · Error
Foreach step cannot iterate a mapping: mappings project data
Error message
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.
What it means
In dynamic workflows, `foreach` executes a concrete step (agent, tool, or plain step) once per item, while a `mapping` entry is a pure data projection executed once. Iterating a mapping is semantically meaningless, so rehydrateWorkflow rejects it with this explanatory error instead of silently producing a broken graph.
Source
Thrown at packages/core/src/workflows/dynamic/rehydrate.ts:130
const date = entry.date instanceof Date ? entry.date : new Date(entry.date);
if (Number.isNaN(date.getTime())) {
throw new Error(`Stored sleepUntil "${entry.id}" has an unparseable date: ${String(entry.date)}`);
}
const live: StepFlowEntry = { type: 'sleepUntil', id: entry.id, date };
wf.__pushStepFlowEntry(live, { type: 'sleepUntil', id: entry.id, date });
return;
}
case 'parallel': {
const live: StepFlowEntry = {
type: 'parallel',
steps: entry.steps.map(s => rehydrateSingleEntry(s, mastra, schemaOpts)),
};
wf.__pushStepFlowEntry(live, entry);
return;
}
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.`,
);
}
const live: StepFlowEntry = {
type: 'foreach',
step: rehydrateSingleEntry(entry.step, mastra, schemaOpts),
opts: { concurrency: entry.opts?.concurrency ?? 1 },
};
wf.__pushStepFlowEntry(live, entry);
return;
}
case 'step': {
const live = rehydrateSingleEntry(entry, mastra, schemaOpts);
wf.__pushStepFlowEntry(live, entry);
return;
}
case 'workflow': {
const nested = assertWorkflowExists(mastra, entry.workflowId);View on GitHub (pinned to 75dd419e61)
Solutions
- Replace the foreach body with an agent, tool, or plain step that processes a single item.
- If a per-item transformation is needed, move the projection into the foreach step's own map/extract of the item, or use a mapping outside the foreach.
- Re-save the corrected graph to storage so the invalid combination is gone.
Example fix
// before
{ type: 'foreach', id: 'f', step: { type: 'mapping', id: 'm', mapConfig: '...' } }
// after
{ type: 'foreach', id: 'f', step: { type: 'tool', id: 't', toolId: 'processItem' } } Defensive patterns
Strategy: validation
Validate before calling
function assertForeachBody(entry) {
if (entry.type === 'foreach' && entry.step?.type === 'mapping') {
throw new TypeError(`Foreach step "${entry.id}" cannot iterate a mapping; use an agent, tool, or plain step`);
}
} Type guard
function hasExecutableForeachBody(e: { type: string; step?: { type?: string } }): boolean {
return e.type !== 'foreach' || (e.step?.type !== undefined && e.step.type !== 'mapping');
} Try / catch
try {
const wf = await rehydrateWorkflow(stored, mastra);
} catch (e) {
if (e instanceof Error && e.message.includes('cannot iterate a mapping')) {
// replace the foreach body with an agent/tool/plain step and re-save
}
throw e;
} Prevention
- Never nest a mapping step as a foreach body when building graphs.
- Constrain builder UIs to executable step types (agent/tool/step) for foreach bodies.
- Validate graph structure before persisting workflows.
- Understand the semantics: mapping projects data once; foreach executes per item.
When it happens
Trigger: A stored workflow graph contains a foreach entry whose `step` field is `{ type: 'mapping', ... }` — e.g. built via a UI that allowed nesting foreach around a map() projection, or a hand-edited stored definition.
Common situations: Builder UI bugs letting users pick a mapping as the foreach body; users confusing map (projection) with foreach (iteration) and modeling a per-item transformation as a mapping; imported definitions from other formats where map-over-items exists.
Related errors
- @mastra/livekit: `workflowInput` is required when `workflow`
- Dynamic workflow bundle contains more than one definition wi
- Dynamic workflow bundle has a circular nested-workflow depen
- SCHEDULES_INVALID_WORKFLOW_PATCH
- ${path} must contain only plain objects.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/160baf26ef9ae6df.
Report an issue: GitHub.