{"record":{"id":"484c72adc383b1d6","repo":"mastra-ai/mastra","slug":"mapping-step-entry-id-cannot-be-stored-the-f","errorCode":null,"errorMessage":"Mapping step \"${entry.id}\" cannot be stored: the function form does not round-trip. Use the declarative form (template / step / initData / value).","messagePattern":"Mapping step \"(.+?)\" cannot be stored: the function form does not round-trip\\. Use the declarative form \\(template / step / initData / value\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/workflows/dynamic/serialize.ts","lineNumber":133,"sourceCode":"      agentId: entry.agentId,\n      description: entry.agent?.description,\n      ...(outputSchema ? { outputSchema } : {}),\n      ...(options ? { options } : {}),\n    };\n  }\n  if (entry.type === 'tool') {\n    const options = pickSerializableStepOptions(entry.options, entry.id, 'tool');\n    return {\n      type: 'tool',\n      id: entry.id,\n      toolId: entry.toolId,\n      description: entry.tool?.description,\n      ...(options ? { options } : {}),\n    };\n  }\n  if (entry.type === 'mapping') {\n    if (typeof entry.mapConfig === 'function') {\n      throw new Error(\n        `Mapping step \"${entry.id}\" cannot be stored: the function form does not round-trip. Use the declarative form (template / step / initData / value).`,\n      );\n    }\n    const serialized: Record<string, any> = {};\n    for (const [key, mapping] of Object.entries(entry.mapConfig as Record<string, any>)) {\n      const m: any = mapping;\n      if (m.fn !== undefined) {\n        throw new Error(`Mapping step \"${entry.id}\" key \"${key}\" cannot be stored: source is a function.`);\n      }\n      if (m.value !== undefined) {\n        serialized[key] = { value: m.value };\n      } else if (m.requestContextPath) {\n        serialized[key] = { requestContextPath: m.requestContextPath };\n      } else if (typeof m.template === 'string') {\n        serialized[key] = { template: m.template };\n      } else if (m.initData) {\n        serialized[key] = { initData: m.initData?.id, path: m.path };\n      } else if (m.step) {","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/workflows/dynamic/serialize.ts#L115-L151","documentation":"A `mapping` step can be declared two ways: a function `(input) => output` (closure) or a declarative config object whose sources are template/step/initData/value/requestContextPath. Only the declarative form survives JSON storage, so `serializeSingleEntry` throws when `mapConfig` is a function. This prevents persisting a workflow whose data projection would silently vanish on reload.","triggerScenarios":"Persisting a workflow containing a mapping entry created with the function form, e.g. `map((data, ctx) => ({...}))` or `{ type: 'mapping', id, mapConfig: fn }`, when `toStorableGraph` runs.","commonSituations":"Authors reach for the function form because it's the most expressive; it works in live runs but breaks persistence (dynamic workflow save, Studio storage). Common in data-reshaping steps between agent/tool calls.","solutions":["Rewrite the mapping declaratively using `template` (string interpolation), `step` (read a prior step's output with `path`), `initData` (read init data by id), `value` (literal), or `requestContextPath` sources.","Split complex projections into a plain custom step executed between steps, then map its output declaratively.","Keep the workflow in-memory only if the function form is essential.","Pre-validate: `typeof entry.mapConfig !== 'function'` before persisting."],"exampleFix":"// before\nmap((data) => ({ name: data.user.name, score: data.result * 100 }))\n\n// after\nmap({\n  name: { step: { step: 'fetchUser', path: 'user.name' } },\n  score: { step: { step: 'evaluate', path: 'result' }, },\n})","handlingStrategy":"type-guard","validationCode":"for (const e of stepFlow) {\n  if (e.type === 'mapping' && typeof e.mapConfig === 'function') {\n    throw new Error(`mapping \"${e.id}\" must use the declarative mapConfig form to be storable`);\n  }\n}","typeGuard":"const hasDeclarativeMapConfig = (e: StepFlowEntry): e is StepFlowEntry & { type: 'mapping'; mapConfig: Record<string, unknown> } =>\n  e.type === 'mapping' && typeof e.mapConfig === 'object' && e.mapConfig !== null;","tryCatchPattern":"try {\n  storable = toStorableGraph(stepFlow);\n} catch (e) {\n  if (/Mapping step .* cannot be stored: the function form/.test(e.message)) {\n    // rewrite the mapping declaratively or move logic to a plain step\n  } else throw e;\n}","preventionTips":["Use template/step/initData/value/requestContextPath sources for all mappings in persisted workflows.","Reserve the function mapping form for ephemeral, in-memory workflows only.","Move complex projections into plain steps."],"tags":["workflows","serialization","closure","mapping"],"backgroundTag":"non-serializable-closure","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}