mastra-ai/mastra · error · FactoryRuleValidationError
${label} must be an object.
Error message
${label} must be an object. What it means
validateBoardRules asserts each rule board (work/review) is a plain object mapping stages to sources. If the whole board value passed via assertFactoryRules is not a plain object (null, array, class instance, primitive), this error names the failing board via the label argument.
Source
Thrown at mastracode/factory/src/rules/validation.ts:143
}
return sanitized;
} finally {
seen.delete(value as object);
}
}
function sanitizeMetadata(value: unknown): Record<string, FactoryRuleJsonValue> | undefined {
if (value === undefined) return undefined;
const sanitized = normalizeFactoryRuleJsonValue(value);
if (!isPlainObject(sanitized)) throw new FactoryRuleValidationError('Rule metadata must be an object.');
if (JSON.stringify(sanitized).length > MAX_METADATA_JSON_LENGTH) {
throw new FactoryRuleValidationError('Rule metadata is too large.');
}
return sanitized;
}
function validateBoardRules(rules: unknown, label: string): asserts rules is FactoryBoardRules {
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.');View on GitHub (pinned to 75dd419e61)
Solutions
- Set the board to an object keyed by valid stage names, e.g. { plan: {} }.
- If a board is unused, pass an empty object {} instead of null.
- Check whether config deserialization (e.g. JSON.parse) produced null for missing keys and default it: rules.work ?? {}.
Example fix
// before
const rules = { version: '1', work: null, review: {}, tools: {}, github: {}, linear: {} };
// after
const rules = { version: '1', work: {}, review: {}, tools: {}, github: {}, linear: {} }; Defensive patterns
Strategy: validation
Validate before calling
function isBoardRules(v) {
return v !== null && typeof v === 'object' && !Array.isArray(v);
}
if (!isBoardRules(rules.work) || !isBoardRules(rules.review)) {
throw new Error('rules.work and rules.review must be plain objects (use {} if empty)');
} Type guard
const isPlainRecord = (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(rules);
} catch (e) {
if (e.name === 'FactoryRuleValidationError' && e.code === 'invalid_factory_rule') {
console.error(`Invalid rules config: ${e.message}`); // fix config and reload
} else throw e;
} Prevention
- Never use null as an 'empty board' placeholder; always use {}.
- Type your config as FactoryRules so TypeScript rejects null at compile time.
- Coerce loaded config with ?? {} defaults for each board before validating.
When it happens
Trigger: Calling assertFactoryRules / prepare / defaultFactoryRules with rules where rules.work or rules.review is null, an array, a Map/class instance, a string, or missing-but-explicitly-non-object (e.g. set to false).
Common situations: Hand-written rules config where work: null as a placeholder; JSON parsed from a config file where a board was omitted and defaulted to null; assigning an array of rules by mistake.
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}.${stage} must be an object.
- ${label}.${stage}.${source} handlers must be functions.
- Factory rules must be an object.
- Factory skill cancelInFlight must be a boolean.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/d813deba3e525c8c.
Report an issue: GitHub.