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
- Batch the work: emit decisions for a bounded slice (e.g. first N items) and let subsequent evaluations handle the rest.
- Aggregate related items into fewer decisions (e.g. one decision referencing a list in metadata) instead of one per item.
- Add an explicit limit/early-return in the rule loop and log when truncating.
- 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
- Cap fan-out loops in rules and process overflow in later evaluations.
- Aggregate related items into fewer decisions where possible.
- Add logging/metrics on decision count per evaluation to catch runaway rules early.
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
- Factory rule version is required.
- ${label} contains an unsupported field.
- Rule metadata is too large.
- ${label} must be an object.
- ${label}.${stage} must be an object.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/82d0566008f57a3f.
Report an issue: GitHub.