mastra-ai/mastra · error · FactoryRuleValidationError
Factory rules must be an object.
Error message
Factory rules must be an object.
What it means
assertFactoryRules is the entry-point validator for a FactoryRules object. Its first check is that the rules argument is a plain object; anything else (null, undefined, array, Map, class instance, string) is rejected before any field-level checks.
Source
Thrown at mastracode/factory/src/rules/validation.ts:161
if (!isPlainObject(rules)) throw new FactoryRuleValidationError(`${label} must be an object.`);
for (const [stage, sources] of Object.entries(rules)) {
enumValue(stage, FACTORY_RULE_STAGES, `${label} stage`);
if (!isPlainObject(sources)) throw new FactoryRuleValidationError(`${label}.${stage} must be an object.`);
for (const [source, leaf] of Object.entries(sources)) {
enumValue(source, FACTORY_RULE_SOURCES, `${label}.${stage} source`);
if (!isPlainObject(leaf)) throw new FactoryRuleValidationError(`${label}.${stage}.${source} must be an object.`);
assertExactKeys(leaf, ['onEnter', 'onExit'], `${label}.${stage}.${source}`);
for (const handler of Object.values(leaf)) {
if (handler !== undefined && typeof handler !== 'function') {
throw new FactoryRuleValidationError(`${label}.${stage}.${source} handlers must be functions.`);
}
}
}
}
}
export function assertFactoryRules(rules: unknown): asserts rules is FactoryRules {
if (!isPlainObject(rules)) throw new FactoryRuleValidationError('Factory rules must be an object.');
assertExactKeys(rules, ['version', 'work', 'review', 'tools', 'github', 'linear'], 'Factory rules');
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)) {View on GitHub (pinned to 75dd419e61)
Solutions
- Ensure a complete rules object is passed, e.g. { version, work, review, tools, github, linear }.
- Guard config loading: const rules = loadedConfig?.rules ?? {}; before validating.
- If using a builder or class instance, convert to a plain object first (e.g. { ...builder.toJSON() }).
Example fix
// before
assertFactoryRules(loadRulesConfig()); // may return null
// after
const config = loadRulesConfig();
assertFactoryRules(config ?? { version: '1', work: {}, review: {}, tools: {}, github: {}, linear: {} }); Defensive patterns
Strategy: try-catch
Validate before calling
function hasCompleteRulesShape(v) {
return v !== null && typeof v === 'object' && !Array.isArray(v) &&
['version', 'work', 'review', 'tools', 'github', 'linear'].every(k => k in v);
}
if (!hasCompleteRulesShape(config?.rules)) throw new Error('rules config missing or malformed'); Type guard
const isFactoryRulesLike = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v) && (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null);
Try / catch
try {
assertFactoryRules(config.rules);
} catch (e) {
if (e.name === 'FactoryRuleValidationError' && e.code === 'invalid_factory_rule') {
console.error('Rules config is not a valid object; falling back to defaults.');
return defaultFactoryRules();
}
throw e;
} Prevention
- Always null-check config loading results before calling assertFactoryRules.
- Use TypeScript's FactoryRules type for the argument so null/undefined are compile errors.
- Provide a checked-in defaultFactoryRules() fallback for missing configs.
When it happens
Trigger: Calling assertFactoryRules / prepare / defaultFactoryRules with null or undefined rules (e.g. a failed config load), an array of rule fragments, or an object created with a non-Object prototype (Object.create(null) is allowed, class instances are not).
Common situations: Config file missing or empty so JSON.parse returns null; forgetting to pass the rules argument; passing a RulesBuilder/Map-like object instead of the plain result.
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 rule version is required.
- ${label} must be an object.
- Cookie password must be at least 32 characters. Set WORKOS_C
- ${label} contains an unsupported field.
- Rule metadata is too large.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/4b868a9d448a73d4.
Report an issue: GitHub.