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

  1. 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.
  2. Split complex projections into a plain custom step executed between steps, then map its output declaratively.
  3. Keep the workflow in-memory only if the function form is essential.
  4. 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

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


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/484c72adc383b1d6. Report an issue: GitHub.