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

  1. Nest the handler under onEvent: rules.github[event] = { onEvent: fn }
  2. Check that every value in rules.github is a plain object literal created with { }, not a function or array
  3. Log Object.entries(rules.github) and typeof each value before calling prepare
  4. 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

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


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