{"record":{"id":"0a34577c7c1d93f1","repo":"mastra-ai/mastra","slug":"path-must-be-json-safe","errorCode":null,"errorMessage":"${path} must be JSON-safe.","messagePattern":"(.+?) must be JSON-safe\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/workflows/builder/index.ts","lineNumber":133,"sourceCode":"  'parallel',\n  'foreach',\n  'sleep',\n  'sleepUntil',\n  'conditional',\n  'loop',\n] as const;\n\nexport type WorkflowBuilderSupportedStepType = (typeof WORKFLOW_BUILDER_SUPPORTED_STEP_TYPES)[number];\n\nexport { WORKFLOW_BUILDER_AUTHORING_CONSTRAINTS, WORKFLOW_BUILDER_AUTHORING_PLAYBOOK } from './authoring-playbook';\n\nfunction normalizeJsonValue(value: unknown, path: string, seen: Set<object>): WorkflowBuilderJsonValue {\n  if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;\n  if (typeof value === 'number') {\n    if (!Number.isFinite(value)) throw new TypeError(`${path} must contain only finite numbers.`);\n    return value;\n  }\n  if (typeof value !== 'object') throw new TypeError(`${path} must be JSON-safe.`);\n  if (seen.has(value)) throw new TypeError(`${path} must not contain cycles.`);\n  seen.add(value);\n  try {\n    if (Array.isArray(value)) return value.map((item, index) => normalizeJsonValue(item, `${path}.${index}`, seen));\n    if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {\n      throw new TypeError(`${path} must contain only plain objects.`);\n    }\n    const normalized: WorkflowBuilderJsonObject = {};\n    for (const [key, item] of Object.entries(value)) {\n      if (item !== undefined) normalized[key] = normalizeJsonValue(item, `${path}.${key}`, seen);\n    }\n    return normalized;\n  } finally {\n    seen.delete(value);\n  }\n}\n\n// OpenAI strict-schema compatibility makes every optional property required and","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/workflows/builder/index.ts#L115-L151","documentation":"normalizeJsonValue() rejects anything that isn't null, string, boolean, finite number, plain object, or array. Values like undefined, functions, Symbols, class instances, Dates, or Maps hit the `typeof value !== 'object'` / non-plain-prototype checks and throw this TypeError with the offending path. The builder enforces strict JSON-safety for all input trees.","triggerScenarios":"Passing undefined, a function, a Symbol, a BigInt, or a non-plain object (Date, Map, Set, class instance) anywhere inside data handed to workflow builder APIs.","commonSituations":"Embedding a Date directly instead of ISO string; passing class instances from ORM results; forgetting that optional fields left as `undefined` are not JSON-safe; including callback functions in config objects.","solutions":["Convert Dates to .toISOString(), Maps/Sets to arrays/records, and class instances to plain objects (structuredClone or toObject)","Filter out undefined fields before passing the object (e.g. JSON.parse(JSON.stringify()) for trusted data, or an explicit omit-undefined pass)","Replace functions/Symbols/BigInts with serializable representations (string names, Number())","Use the reported `path` to locate the exact offending value"],"exampleFix":"// before\nbuilder.withParams({ createdAt: new Date(), onUpdate: () => {} });\n// after\nbuilder.withParams({ createdAt: new Date().toISOString() });","handlingStrategy":"validation","validationCode":"const isJsonSafe = (v) => v === null || ['string','boolean','number'].includes(typeof v) || (typeof v === 'object' && !Array.isArray(v) === false || v.constructor === Object);\nfunction deepSanitize(o) { return JSON.parse(JSON.stringify(o, (k, v) => v === undefined ? null : v)); }","typeGuard":"function isPlainObject(v) { return typeof v === 'object' && v !== null && (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null); }","tryCatchPattern":"try {\n  builder.withParams(raw);\n} catch (e) {\n  if (e instanceof TypeError && e.message.includes('must be JSON-safe')) {\n    builder.withParams(deepSanitize(raw));\n  } else throw e;\n}","preventionTips":["Convert Dates to ISO strings, Maps/Sets to plain structures","Never pass functions, Symbols, or BigInts in builder input","Strip undefined fields before submission","Prefer explicit mapping to plain objects over passing ORM/class instances"],"tags":["validation","json","workflow-builder"],"backgroundTag":"non-serializable-value","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}