mastra-ai/mastra · error · FactoryRuleValidationError

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.

Solutions

  1. Set the board to an object keyed by valid stage names, e.g. { plan: {} }.
  2. If a board is unused, pass an empty object {} instead of null.
  3. 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

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


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/d813deba3e525c8c. Report an issue: GitHub.

Appendix: 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)