mastra-ai/mastra · error · Error
Mapping step "${entry.id}" cannot be stored: the function fo
Error message
Mapping step "${entry.id}" cannot be stored: the function form does not round-trip. Use the declarative form (template / step / initData / value). What it means
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.
Source
Thrown at packages/core/src/workflows/dynamic/serialize.ts:133
agentId: entry.agentId,
description: entry.agent?.description,
...(outputSchema ? { outputSchema } : {}),
...(options ? { options } : {}),
};
}
if (entry.type === 'tool') {
const options = pickSerializableStepOptions(entry.options, entry.id, 'tool');
return {
type: 'tool',
id: entry.id,
toolId: entry.toolId,
description: entry.tool?.description,
...(options ? { options } : {}),
};
}
if (entry.type === 'mapping') {
if (typeof entry.mapConfig === 'function') {
throw new Error(
`Mapping step "${entry.id}" cannot be stored: the function form does not round-trip. Use the declarative form (template / step / initData / value).`,
);
}
const serialized: Record<string, any> = {};
for (const [key, mapping] of Object.entries(entry.mapConfig as Record<string, any>)) {
const m: any = mapping;
if (m.fn !== undefined) {
throw new Error(`Mapping step "${entry.id}" key "${key}" cannot be stored: source is a function.`);
}
if (m.value !== undefined) {
serialized[key] = { value: m.value };
} else if (m.requestContextPath) {
serialized[key] = { requestContextPath: m.requestContextPath };
} else if (typeof m.template === 'string') {
serialized[key] = { template: m.template };
} else if (m.initData) {
serialized[key] = { initData: m.initData?.id, path: m.path };
} else if (m.step) {View on GitHub (pinned to 75dd419e61)
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.
Example fix
// before
map((data) => ({ name: data.user.name, score: data.result * 100 }))
// after
map({
name: { step: { step: 'fetchUser', path: 'user.name' } },
score: { step: { step: 'evaluate', path: 'result' }, },
}) Defensive patterns
Strategy: type-guard
Validate before calling
for (const e of stepFlow) {
if (e.type === 'mapping' && typeof e.mapConfig === 'function') {
throw new Error(`mapping "${e.id}" must use the declarative mapConfig form to be storable`);
}
} Type guard
const hasDeclarativeMapConfig = (e: StepFlowEntry): e is StepFlowEntry & { type: 'mapping'; mapConfig: Record<string, unknown> } =>
e.type === 'mapping' && typeof e.mapConfig === 'object' && e.mapConfig !== null; Try / catch
try {
storable = toStorableGraph(stepFlow);
} catch (e) {
if (/Mapping step .* cannot be stored: the function form/.test(e.message)) {
// rewrite the mapping declaratively or move logic to a plain step
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Mapping step "${entry.id}" key "${key}" cannot be stored: so
- SleepUntil step "${entry.id}" cannot be stored: dynamic date
- Conditional (branch) step cannot be stored: closure predicat
- Loop step "${getSingleStepEntryId(entry.step)}" cannot be st
- ${kind === 'agent' ? 'Agent' : 'Tool'} step "${entryId}" can
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/484c72adc383b1d6.
Report an issue: GitHub.