mastra-ai/mastra · error · FactoryRuleValidationError
Factory decisions require unique idempotency keys.
Error message
Factory decisions require unique idempotency keys.
What it means
Every decision in a validated batch must carry a distinct idempotencyKey. After validating each decision, validateFactoryRuleDecisions compares the Set size of the collected keys against the array length and throws on any duplicate, since idempotency keys are used to dedupe/track committed work — duplicates would silently collapse or double-apply decisions.
Source
Thrown at mastracode/factory/src/rules/validation.ts:372
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
- Make each idempotencyKey unique by including the discriminating identifiers (item id, event id, timestamp/nonce).
- Deduplicate decisions before returning (e.g. key by idempotencyKey in a Map and emit the last/first winner).
- For genuinely repeated work, keep one decision and merge the payloads instead of emitting duplicates.
Example fix
// before
issues.map(i => ({ type: 'link', idempotencyKey: 'link-issue', ... }))
// after
issues.map(i => ({ type: 'link', idempotencyKey: `link-issue:${i.id}`, ... })) Defensive patterns
Strategy: validation
Validate before calling
function checkUniqueKeys(decisions) {
const keys = decisions.map(d => d.idempotencyKey);
if (new Set(keys).size !== keys.length) {
throw new Error('duplicate idempotencyKey in decision batch');
}
} Type guard
null
Try / catch
try {
return validateFactoryRuleDecisions(decisions);
} catch (e) {
if (e instanceof FactoryRuleValidationError && /unique idempotency keys/.test(e.message)) {
const seen = new Set();
return validateFactoryRuleDecisions(decisions.filter(d => !seen.has(d.idempotencyKey) && seen.add(d.idempotencyKey)));
}
throw e;
} Prevention
- Build keys from the most specific identifiers available (item id + event id).
- Never hardcode idempotencyKey inside loops; interpolate the loop variable.
- Deduplicate by key (Map) before returning decisions.
When it happens
Trigger: A rule emitting multiple decisions where two or more share the same idempotencyKey — e.g. building keys from a coarse template like `${board}:${stage}` without including the item id, or reusing the same constant key in a loop.
Common situations: Loop-generated decisions that forgot to interpolate the loop item into the key; rules processing multiple events whose key template omits the event id; copy-pasted decision constructors with a hardcoded key.
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/d288755a0bc0e590.
Report an issue: GitHub.