mastra-ai/mastra · error · FactoryRuleValidationError

Factory rule produced too many decisions.

Error message

Factory rule produced too many decisions.

What it means

validateFactoryRuleDecisions bounds how many decisions a single rule evaluation may return, rejecting arrays longer than MAX_JSON_COLLECTION_SIZE before validating each entry. This protects the factory pipeline from runaway rules flooding the dispatcher (and downstream storage/queues) with unbounded work.

Source

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

        value.level === undefined
          ? undefined
          : enumValue(value.level, ['info', 'warning', 'error'] as const, 'Factory notification level');
      return {
        type,
        ...commonCommitFields(value),
        title: boundedString(value.title, 'Factory notification title', MAX_TITLE_LENGTH),
        ...(body ? { body } : {}),
        ...(level ? { level } : {}),
      };
    }
    default:
      throw new FactoryRuleValidationError('Factory rule decision type is unsupported.');
  }
}

export function validateFactoryRuleDecisions(values: readonly unknown[], causalDepth = 0): FactoryCommitDecision[] {
  if (values.length > MAX_JSON_COLLECTION_SIZE) {
    throw new FactoryRuleValidationError('Factory rule produced too many decisions.');
  }
  const decisions: FactoryCommitDecision[] = [];
  for (const value of values) {
    const decision = validateFactoryRuleDecision(value, causalDepth);
    if (decision.type === 'reject') {
      throw new FactoryRuleValidationError('A rejection cannot be persisted with commit decisions.');
    }
    decisions.push(decision);
  }
  const keys = decisions.map(decision => decision.idempotencyKey);
  if (new Set(keys).size !== keys.length) {
    throw new FactoryRuleValidationError('Factory decisions require unique idempotency keys.');
  }
  return decisions;
}

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Batch the work: emit decisions for a bounded slice (e.g. first N items) and let subsequent evaluations handle the rest.
  2. Aggregate related items into fewer decisions (e.g. one decision referencing a list in metadata) instead of one per item.
  3. Add an explicit limit/early-return in the rule loop and log when truncating.
  4. If the workload is legitimately huge, run it through multiple rule evaluations keyed by page/cursor.

Example fix

// before
return issues.map(i => makeDecision(i)) // unbounded
// after
const BATCH = 50;
return issues.slice(0, BATCH).map(i => makeDecision(i));
Defensive patterns

Strategy: validation

Validate before calling

const MAX_JSON_COLLECTION_SIZE = 100; // match library cap
function checkBatch(decisions) {
  if (decisions.length > MAX_JSON_COLLECTION_SIZE) {
    throw new Error(`Rule produced ${decisions.length} decisions; cap is ${MAX_JSON_COLLECTION_SIZE}`);
  }
}

Type guard

null

Try / catch

try {
  return validateFactoryRuleDecisions(decisions);
} catch (e) {
  if (e instanceof FactoryRuleValidationError && /too many decisions/.test(e.message)) {
    return chunkAndSchedule(decisions); // persist overflow for later evaluations
  }
  throw e;
}

Prevention

When it happens

Trigger: A rule returning more than MAX_JSON_COLLECTION_SIZE decisions from evaluate, #ingestIssue, ingestToolResult, validated, or transition — e.g. fan-out over a large issue list or a loop emitting one decision per item in a huge collection.

Common situations: Rules that iterate an entire backlog or a large webhook payload without batching; recursive/expanded rules generating a decision per combination of inputs; a bug causing an unbounded loop to emit decisions.

Related errors


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