mastra-ai/mastra · error · Error
resolved to a value that could not be JSON-stringified ( )…
Error message
${describeBadPlaceholder(template, idx, rawExpr)} resolved to a value that could not be JSON-stringified (${(err as Error).message}). Drill into a primitive path (e.g. ${${rawExpr}.someField}) or reshape the value in a preceding step. What it means
When a template placeholder resolves to an object (or array), it is serialized with JSON.stringify for interpolation. If the resolved value cannot be stringified (e.g. circular references or BigInt values), stringifyTemplateValue throws advising the user to drill into a primitive field or reshape the value upstream.
Solutions
- Reference a concrete primitive path instead of the whole object: "${stepResults.myStep.id}".
- Convert BigInt/circular fields to strings/numbers in the producing step before mapping.
- If the whole object is needed, JSON-serialize it safely in the prior step (e.g. a JSON.stringify with replacer) and reference that string.
Example fix
// before
prompt.push('${stepResults.dbRow}'); // row contains BigInt
// after
prompt.push('${stepResults.dbRow.id}'); Defensive patterns
Strategy: validation
Validate before calling
function isJsonSerializable(v: unknown): boolean {
try { JSON.stringify(v); return true; } catch { return false; }
}
// before mapping: if (!isJsonSerializable(stepOutput)) normalize it upstream Type guard
function isPlainSerializable(v: unknown): boolean {
if (typeof v === 'bigint') return false;
if (typeof v !== 'object' || v === null) return true;
try { JSON.stringify(v); return true; } catch { return false; }
} Try / catch
try {
run.workflow.mapVariable({ value: '${stepResults.myStep}' });
} catch (err) {
if (err instanceof Error && err.message.includes('could not be JSON-stringified')) {
console.error('Value contains BigInt/circular refs; drill into a primitive path');
} else throw err;
} Prevention
- Interpolate primitive fields, not whole objects, into string templates.
- Convert BigInt values to string/number at step boundaries (e.g. DB/ORM rows).
- Avoid returning class instances with circular references from steps.
When it happens
Trigger: A placeholder like "${stepResults.myStep}" resolves to an object containing a circular reference or BigInt (e.g. a BigInt field, class instances with cyclic refs), and the value is interpolated into a string template.
Common situations: Steps returning database rows/ORM entities with cycles; BigInt IDs from blockchain or DB drivers; returning whole response objects instead of primitive fields into string templates.
Understand the failure class
Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.
Related errors
- Cannot rehydrate conditional step: missing or mismatched…
- Cannot rehydrate loop step: missing declarative predicate…
- DATASET_ITEM_PAYLOAD_NOT_SERIALIZABLE
- has empty or whitespace-padded contents. Use with no…
- must be of the form or .
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/37781d6d9b5ea3da.
Report an issue: GitHub.
Appendix: source
Thrown at packages/core/src/workflows/mapping-template.ts:123
}
return ids;
}
/**
* Coerces a resolved placeholder value to a string. Primitives are stringified
* the normal way; objects and arrays are JSON-encoded so downstream agents can
* consume complex step outputs (e.g. `foreach(agent)` returns `{ text }[]`)
* directly in a template. `null`/`undefined` render as empty. If JSON encoding
* fails (circular references, BigInt, etc.), throws with a hint pointing at
* the offending placeholder.
*/
function stringifyTemplateValue(v: unknown, template: string, idx: number, rawExpr: string): string {
if (v === null || v === undefined) return '';
if (typeof v === 'object') {
try {
return JSON.stringify(v);
} catch (err) {
throw new Error(
`${describeBadPlaceholder(template, idx, rawExpr)} resolved to a value that could not be JSON-stringified ` +
`(${(err as Error).message}). Drill into a primitive path (e.g. \${${rawExpr}.someField}) or reshape the value in a preceding step.`,
);
}
}
return String(v);
}
/**
* Resolves `${<scope>.<path>}` placeholders against the implicit namespaces
* available in a step's execute context. See the `.map()` overload signature
* for the full list of accepted scopes (`inputData`, `initData`, `state`,
* `requestContext`, `stepResults.<stepId>`).
*/
export function resolveTemplate(template: string, ctx: any): string {
let idx = 0;
return template.replace(TEMPLATE_PLACEHOLDER, (_match, rawExpr: string) => {
idx++;View on GitHub (pinned to 75dd419e61)