mastra-ai/mastra · error · Error
Mapping step "${entry.id}" key "${key}" cannot be stored: so
Error message
Mapping step "${entry.id}" key "${key}" cannot be stored: source is a function. What it means
Within a declarative mapping config, each key's source must be serializable data (value/template/requestContextPath/initData/step). A source written as `fn: (data, ctx) => ...` is a closure that cannot be persisted, so serialization of that specific key fails. The error identifies both the mapping step id and the offending key.
Source
Thrown at packages/core/src/workflows/dynamic/serialize.ts:141
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) {
serialized[key] = {
step: Array.isArray(m.step) ? m.step.map((s: any) => s?.id) : m.step?.id,
path: m.path,
};
} else {
serialized[key] = m;
}
}View on GitHub (pinned to 75dd419e61)
Solutions
- Replace the `fn` source with a declarative equivalent: `template`, `value`, `step` + `path`, `initData`, or `requestContextPath`.
- Compute derived values in a preceding plain step and reference its output via a `step` source with a `path`.
- Remove the computed key entirely if it can be derived downstream at consumption time.
- Pre-validate each key: `Object.values(mapConfig).every(m => m.fn === undefined)`.
Example fix
// before
map({ total: { fn: (d) => d.price * d.qty } })
// after (compute in a prior step, then)
map({ total: { step: { step: 'computeTotal', path: 'total' } } }) Defensive patterns
Strategy: validation
Validate before calling
for (const e of stepFlow) {
if (e.type === 'mapping' && typeof e.mapConfig === 'object') {
for (const [key, m] of Object.entries(e.mapConfig)) {
if ((m as any)?.fn !== undefined) throw new Error(`mapping "${e.id}" key "${key}" uses a fn source`);
}
}
} Type guard
const isFnSource = (m: unknown): m is { fn: Function } =>
!!m && typeof m === 'object' && typeof (m as any).fn === 'function'; Try / catch
try {
storable = toStorableGraph(stepFlow);
} catch (e) {
if (/cannot be stored: source is a function/.test(e.message)) {
// replace the named key's fn source with a declarative source
} else throw e;
} Prevention
- Audit every mapping key for `fn` sources before saving.
- Compute derived values in a prior step and read them via `step` + `path` sources.
- Keep mapping sources strictly data-declarative in persisted workflows.
When it happens
Trigger: Persisting a mapping entry whose declarative `mapConfig` object contains at least one key with a `fn` source, e.g. `map({ total: { fn: (d) => d.a + d.b } })`.
Common situations: Mixed usage: authors convert most keys to declarative sources but keep one computed field as `fn`, assuming partial closures are tolerated. Persistence then rejects the whole step.
Related errors
- Mapping step "${entry.id}" cannot be stored: the function fo
- 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/d4d089a0946081f9.
Report an issue: GitHub.