mastra-ai/mastra · error · FactoryRuleValidationError
Factory rules.github.${event} must be an object.
Error message
Factory rules.github.${event} must be an object. What it means
This error is thrown by assertFactoryRules when a per-event entry under rules.github is not a plain object. Each GitHub event key in the factory rules config must map to an object holding at most an `onEvent` handler. The library validates the full rules shape up front so invalid rule configs fail fast at prepare/defaultFactoryRules time instead of silently misbehaving at event time.
Source
Thrown at mastracode/factory/src/rules/validation.ts:181
boundedString(rules.version, 'Factory rule version', MAX_VERSION_LENGTH);
validateBoardRules(rules.work, 'Factory rules.work');
validateBoardRules(rules.review, 'Factory rules.review');
if (!isPlainObject(rules.tools)) throw new FactoryRuleValidationError('Factory rules.tools must be an object.');
for (const [toolName, leaf] of Object.entries(rules.tools)) {
boundedString(toolName, 'Factory tool name', 128, IDENTIFIER_RE);
if (!isPlainObject(leaf))
throw new FactoryRuleValidationError(`Factory rules.tools.${toolName} must be an object.`);
assertExactKeys(leaf, ['onResult'], `Factory rules.tools.${toolName}`);
if (leaf.onResult !== undefined && typeof leaf.onResult !== 'function') {
throw new FactoryRuleValidationError(`Factory rules.tools.${toolName}.onResult must be a function.`);
}
}
if (!isPlainObject(rules.github)) throw new FactoryRuleValidationError('Factory rules.github must be an object.');
for (const [event, leaf] of Object.entries(rules.github)) {
enumValue(event, FACTORY_GITHUB_EVENTS, 'Factory GitHub event');
if (!isPlainObject(leaf)) throw new FactoryRuleValidationError(`Factory rules.github.${event} must be an object.`);
assertExactKeys(leaf, ['onEvent'], `Factory rules.github.${event}`);
if (leaf.onEvent !== undefined && typeof leaf.onEvent !== 'function') {
throw new FactoryRuleValidationError(`Factory rules.github.${event}.onEvent must be a function.`);
}
}
if (!isPlainObject(rules.linear)) throw new FactoryRuleValidationError('Factory rules.linear must be an object.');
for (const [event, leaf] of Object.entries(rules.linear)) {
enumValue(event, FACTORY_LINEAR_EVENTS, 'Factory Linear event');
if (!isPlainObject(leaf)) throw new FactoryRuleValidationError(`Factory rules.linear.${event} must be an object.`);
assertExactKeys(leaf, ['onEvent'], `Factory rules.linear.${event}`);
if (leaf.onEvent !== undefined && typeof leaf.onEvent !== 'function') {
throw new FactoryRuleValidationError(`Factory rules.linear.${event}.onEvent must be a function.`);
}
}
}
function commonCommitFields(value: Record<string, unknown>): { idempotencyKey: string } {View on GitHub (pinned to 75dd419e61)
Solutions
- Nest the handler under onEvent: rules.github[event] = { onEvent: fn }
- Check that every value in rules.github is a plain object literal created with { }, not a function or array
- Log Object.entries(rules.github) and typeof each value before calling prepare
- If config comes from JSON/env, remember functions cannot survive serialization; attach onEvent in code after loading config
Example fix
// before
prepare({ rules: { github: { issue_commented: (ctx) => {} } } })
// after
prepare({ rules: { github: { issue_commented: { onEvent: (ctx) => {} } } } }) Defensive patterns
Strategy: validation
Validate before calling
function assertGithubRules(rules) {
if (rules?.github) {
for (const [event, leaf] of Object.entries(rules.github)) {
if (leaf === null || typeof leaf !== 'object' || Array.isArray(leaf)) {
throw new TypeError(`rules.github.${event} must be a plain object`);
}
}
}
} Type guard
const isPlainObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v); const isGithubRuleLeaf = (v) => isPlainObject(v);
Try / catch
try {
prepare({ rules });
} catch (e) {
if (e instanceof FactoryRuleValidationError && e.message.includes('rules.github.')) {
console.error('Invalid github rule entry:', e.message);
} else throw e;
} Prevention
- Always wrap handlers in { onEvent: fn }
- Never build github rule values as arrays or functions
- Validate config with a zod schema before prepare
- Attach function handlers in code, not via serialized JSON config
When it happens
Trigger: Calling prepare(...) or defaultFactoryRules(...) with rules.github containing a value for an event key that is a function, string, number, array, null, or class instance instead of a plain object literal, e.g. rules: { github: { issue_commented: myHandler } }.
Common situations: Assigning the onEvent callback directly to the event key instead of nesting it inside an object; passing an array of handlers; JSON round-trips turning functions into strings; reusing a config object where the event key was overwritten by a handler.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Factory rules.linear must be an object.
- Factory rules.linear.${event} must be an object.
- Google RBAC roleMapping is required.
- Cookie password must be at least 32 characters. Set OKTA_COO
- heartbeatMs must be a finite number no greater than ${MAX_TI
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/796b3f313c2dde12.
Report an issue: GitHub.