{"record":{"id":"d4d089a0946081f9","repo":"mastra-ai/mastra","slug":"mapping-step-entry-id-key-key-cannot-be","errorCode":null,"errorMessage":"Mapping step \"${entry.id}\" key \"${key}\" cannot be stored: source is a function.","messagePattern":"Mapping step \"(.+?)\" key \"(.+?)\" cannot be stored: source is a function\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/workflows/dynamic/serialize.ts","lineNumber":141,"sourceCode":"    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) {\n        serialized[key] = {\n          step: Array.isArray(m.step) ? m.step.map((s: any) => s?.id) : m.step?.id,\n          path: m.path,\n        };\n      } else {\n        serialized[key] = m;\n      }\n    }","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/workflows/dynamic/serialize.ts#L123-L159","documentation":"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.","triggerScenarios":"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 } })`.","commonSituations":"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.","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)`."],"exampleFix":"// before\nmap({ total: { fn: (d) => d.price * d.qty } })\n\n// after (compute in a prior step, then)\nmap({ total: { step: { step: 'computeTotal', path: 'total' } } })","handlingStrategy":"validation","validationCode":"for (const e of stepFlow) {\n  if (e.type === 'mapping' && typeof e.mapConfig === 'object') {\n    for (const [key, m] of Object.entries(e.mapConfig)) {\n      if ((m as any)?.fn !== undefined) throw new Error(`mapping \"${e.id}\" key \"${key}\" uses a fn source`);\n    }\n  }\n}","typeGuard":"const isFnSource = (m: unknown): m is { fn: Function } =>\n  !!m && typeof m === 'object' && typeof (m as any).fn === 'function';","tryCatchPattern":"try {\n  storable = toStorableGraph(stepFlow);\n} catch (e) {\n  if (/cannot be stored: source is a function/.test(e.message)) {\n    // replace the named key's fn source with a declarative source\n  } else throw e;\n}","preventionTips":["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."],"tags":["workflows","serialization","closure","mapping"],"backgroundTag":"non-serializable-closure","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}