mastra-ai/mastra · error
${path} must contain only finite numbers.
Error message
${path} must contain only finite numbers. What it means
Workflow builder input must be JSON-serializable. normalizeJsonValue() recursively validates values, and any number that is NaN, +Infinity, or -Infinity fails Number.isFinite() and triggers this TypeError naming the offending path. NaN/Infinity cannot round-trip through JSON, so the builder rejects them at the boundary.
Source
Thrown at packages/core/src/workflows/builder/index.ts:130
'tool',
'mapping',
'workflow',
'parallel',
'foreach',
'sleep',
'sleepUntil',
'conditional',
'loop',
] as const;
export type WorkflowBuilderSupportedStepType = (typeof WORKFLOW_BUILDER_SUPPORTED_STEP_TYPES)[number];
export { WORKFLOW_BUILDER_AUTHORING_CONSTRAINTS, WORKFLOW_BUILDER_AUTHORING_PLAYBOOK } from './authoring-playbook';
function normalizeJsonValue(value: unknown, path: string, seen: Set<object>): WorkflowBuilderJsonValue {
if (value === null || typeof value === 'string' || typeof value === 'boolean') return value;
if (typeof value === 'number') {
if (!Number.isFinite(value)) throw new TypeError(`${path} must contain only finite numbers.`);
return value;
}
if (typeof value !== 'object') throw new TypeError(`${path} must be JSON-safe.`);
if (seen.has(value)) throw new TypeError(`${path} must not contain cycles.`);
seen.add(value);
try {
if (Array.isArray(value)) return value.map((item, index) => normalizeJsonValue(item, `${path}.${index}`, seen));
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
throw new TypeError(`${path} must contain only plain objects.`);
}
const normalized: WorkflowBuilderJsonObject = {};
for (const [key, item] of Object.entries(value)) {
if (item !== undefined) normalized[key] = normalizeJsonValue(item, `${path}.${key}`, seen);
}
return normalized;
} finally {
seen.delete(value);
}View on GitHub (pinned to 75dd419e61)
Solutions
- Sanitize inputs with Number.isFinite checks and replace non-finite values with a defined fallback (0, null, or a sentinel)
- Trace the reported `path` to find which field holds NaN/Infinity and fix the producing computation
- If infinity is meaningful, encode it as a string or { finite: false } marker the schema accepts
- Clamp or guard the arithmetic (avoid division by zero, coerce parseInt with Number.isNaN fallback)
Example fix
// before
const rate = total / count; // count = 0 → Infinity
builder.withConfig({ rate });
// after
const rate = count > 0 ? total / count : 0;
if (!Number.isFinite(rate)) throw new Error('invalid rate');
builder.withConfig({ rate }); Defensive patterns
Strategy: validation
Validate before calling
function assertFiniteNumbers(obj, path = 'input') {
for (const [k, v] of Object.entries(obj)) {
if (typeof v === 'number' && !Number.isFinite(v)) throw new Error(`${path}.${k} is not finite`);
if (v && typeof v === 'object') assertFiniteNumbers(v, `${path}.${k}`);
}
}
assertFiniteNumbers(params); Type guard
const isFiniteNumber = (v) => typeof v === 'number' && Number.isFinite(v);
Try / catch
try {
builder.withParams(raw);
} catch (e) {
if (e instanceof TypeError && e.message.includes('must contain only finite numbers')) {
logger.error('Non-finite number at', e.message.split(' ')[0]);
} else throw e;
} Prevention
- Guard division: check divisors for 0
- Check Number.isNaN after parseInt/parseFloat with fallbacks
- Sanitize computed defaults before passing to builder APIs
- Treat the thrown `path` message as your pointer to the bad field
When it happens
Trigger: Passing data containing NaN, Infinity, or -Infinity into workflow builder APIs (e.g. default/params/config values), typically from a division by zero, parseInt failure, or a Math op returning NaN.
Common situations: Computing defaults at runtime (e.g. 1/0, parseInt(undefined)); deserializing values from a source that encoded Infinity as a bare token; stats/averages over empty arrays yielding NaN.
Related errors
- ${path} must be JSON-safe.
- ${path} must not contain cycles.
- Rule metadata must contain finite numbers.
- Rule metadata is not bounded JSON.
- Rule metadata must not contain cycles.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/a10d7bf500a5d0fc.
Report an issue: GitHub.