mastra-ai/mastra · error · FactoryRuleValidationError

Rule metadata must contain finite numbers.

Error message

Rule metadata must contain finite numbers.

What it means

Rule metadata (the metadata field of an upsertLinkedWorkItem decision) must be JSON-serializable with finite numbers only. normalizeFactoryRuleJsonValue rejects NaN, Infinity, and -Infinity because they do not survive JSON.stringify (they become null) and indicate numeric bugs. The library throws this before persisting metadata.

Source

Thrown at mastracode/factory/src/rules/validation.ts:99

  if (value === undefined) return undefined;
  return boundedString(value, label, max);
}

function enumValue<T extends string>(value: unknown, allowed: readonly T[], label: string): T {
  if (typeof value !== 'string' || !allowed.includes(value as T)) {
    throw new FactoryRuleValidationError(`${label} is invalid.`);
  }
  return value as T;
}

export function normalizeFactoryRuleJsonValue(
  value: unknown,
  depth = 0,
  seen = new Set<object>(),
): FactoryRuleJsonValue {
  if (value === null || typeof value === 'boolean' || typeof value === 'string') return value;
  if (typeof value === 'number') {
    if (!Number.isFinite(value)) throw new FactoryRuleValidationError('Rule metadata must contain finite numbers.');
    return value;
  }
  if (depth >= MAX_JSON_DEPTH || (typeof value !== 'object' && !Array.isArray(value))) {
    throw new FactoryRuleValidationError('Rule metadata is not bounded JSON.');
  }
  if (seen.has(value as object)) throw new FactoryRuleValidationError('Rule metadata must not contain cycles.');
  seen.add(value as object);
  try {
    if (Array.isArray(value)) {
      if (value.length > MAX_JSON_COLLECTION_SIZE) {
        throw new FactoryRuleValidationError('Rule metadata contains too many entries.');
      }
      return value.map(entry => normalizeFactoryRuleJsonValue(entry, depth + 1, seen));
    }
    if (!isPlainObject(value)) throw new FactoryRuleValidationError('Rule metadata must use plain objects.');
    const entries = Object.entries(value);
    if (entries.length > MAX_JSON_COLLECTION_SIZE) {
      throw new FactoryRuleValidationError('Rule metadata contains too many fields.');

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Check Number.isFinite(value) before placing computed numbers into metadata, and substitute a fallback (e.g. 0 or null).
  2. Replace NaN/Infinity with null (null is allowed) or a sentinel string.
  3. Fix the upstream arithmetic (guard division by zero, validate parsed input).
  4. Sanitize metadata through a JSON round-trip JSON.parse(JSON.stringify(value)) and inspect what becomes null.

Example fix

// before
metadata: { coverage: passed / total }
// after
metadata: { coverage: total > 0 ? passed / total : null }
Defensive patterns

Strategy: validation

Validate before calling

const assertFiniteNumbers = (obj: unknown, depth = 0): void => {
  if (typeof obj === 'number' && !Number.isFinite(obj)) throw new RangeError('metadata contains non-finite number');
  if (obj && typeof obj === 'object') for (const v of Object.values(obj)) assertFiniteNumbers(v, depth + 1);
};
assertFiniteNumbers(metadata);

Type guard

const isFiniteNumber = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v);

Try / catch

try {
  decisions.push({ type: 'upsertLinkedWorkItem', metadata, /* ... */ });
} catch (e) {
  if (e instanceof FactoryRuleValidationError && e.message.includes('finite numbers')) {
    metadata = JSON.parse(JSON.stringify(metadata, (k, v) => (typeof v === 'number' && !Number.isFinite(v) ? null : v)));
  } else throw e;
}

Prevention

When it happens

Trigger: Passing metadata with values like { attempts: NaN } or { ratio: 1/0 }, often the result of division by zero, parseInt on a bad string, or undefined arithmetic in the rule code producing NaN.

Common situations: Computing durations/ratios in rule handlers where a denominator is 0, parsing malformed numeric input with Number() returning NaN, spreading objects where a field was lost and NaN results.

Related errors


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