{"record":{"id":"532b6ad26b25f72e","repo":"mastra-ai/mastra","slug":"foreach-step-cannot-iterate-a-mapping-mappings-pr-532b6a","errorCode":null,"errorMessage":"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.","messagePattern":"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\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/workflows/dynamic/serialize.ts","lineNumber":60,"sourceCode":"    case 'agent':\n    case 'tool':\n    case 'mapping':\n      return serializeSingleEntry(entry);\n    case 'sleep':\n      if (typeof entry.duration !== 'number') {\n        throw new Error(`Sleep step \"${entry.id}\" cannot be stored: dynamic duration (function) is not supported.`);\n      }\n      return { type: 'sleep', id: entry.id, duration: entry.duration };\n    case 'sleepUntil':\n      if (!(entry.date instanceof Date)) {\n        throw new Error(`SleepUntil step \"${entry.id}\" cannot be stored: dynamic date (function) is not supported.`);\n      }\n      return { type: 'sleepUntil', id: entry.id, date: entry.date };\n    case 'parallel':\n      return { type: 'parallel', steps: entry.steps.map(s => serializeSingleEntry(s)) };\n    case 'foreach':\n      if (entry.step.type === 'mapping') {\n        throw new Error(\n          `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.`,\n        );\n      }\n      return {\n        type: 'foreach',\n        step: serializeSingleEntry(entry.step),\n        opts:\n          typeof entry.opts.concurrency === 'function'\n            ? { fn: entry.opts.concurrency.toString() }\n            : { concurrency: entry.opts.concurrency },\n      };\n    case 'conditional': {\n      const predicates = entry.predicates;\n      if (!predicates || predicates.some(p => !p || typeof p !== 'object')) {\n        throw new Error(\n          `Conditional (branch) step cannot be stored: closure predicates do not round-trip. Use the declarative form ({ predicate: {...} }) for each branch.`,\n        );\n      }","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/workflows/dynamic/serialize.ts#L42-L78","documentation":"A `foreach` entry's body must be an executable step (agent, tool, or plain step) that runs once per item. A `mapping` entry is a data projection — it reshapes input once, not per item — so using it as a foreach body is semantically invalid. The serializer rejects it at `toStorableGraph` time instead of producing a persisted workflow that behaves incorrectly on reload.","triggerScenarios":"Building a workflow where `.foreach()` (or the dynamic `foreach` entry constructor) is given a `mapping`-typed step entry as its body, then calling `toStorableGraph` on the resulting `stepFlow`.","commonSituations":"Developers want to transform each item of a list and reach for a mapping step inside foreach, confusing 'project data' with 'execute per item'. Usually surfaces when persisting a dynamically built batch-processing workflow.","solutions":["Use an agent, tool, or plain step as the foreach body; move the per-item projection into that step's logic or into a mapping step placed before/after the foreach.","Restructure: `mapping` (project the list) -> `foreach(agent/tool/step)` -> `mapping` (collect results).","Guard before persisting: `entry.type === 'foreach' && entry.step.type !== 'mapping'`."],"exampleFix":"// before\nforeach({ type: 'mapping', id: 'proj', mapConfig: {...} })\n\n// after\nforeach({ type: 'tool', id: 'process-item', toolId: 'myTool' })","handlingStrategy":"validation","validationCode":"for (const e of stepFlow) {\n  if (e.type === 'foreach' && e.step.type === 'mapping') {\n    throw new Error('foreach body must be agent/tool/step, not a mapping');\n  }\n}","typeGuard":"const hasExecutableBody = (e: StepFlowEntry): boolean =>\n  e.type !== 'foreach' || (e.step.type === 'agent' || e.step.type === 'tool' || e.step.type === 'step');","tryCatchPattern":"try {\n  storable = toStorableGraph(stepFlow);\n} catch (e) {\n  if (/Foreach step cannot iterate a mapping/.test(e.message)) {\n    // rebuild the foreach with an executable body\n  } else throw e;\n}","preventionTips":["Remember: mapping projects data once; foreach executes per item.","Put mappings before/after the foreach, not inside it.","Use the supported entry factories rather than hand-assembling entries."],"tags":["workflows","serialization","foreach","misuse"],"backgroundTag":"invalid-step-composition","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}