mastra-ai/mastra · error · FactoryRuleValidationError

Factory rule decision must be an object.

Error message

Factory rule decision must be an object.

What it means

validateFactoryRuleDecision requires its argument to be a plain object representing a rule decision (with a `type` field). If value is not a plain object (null, array, string, function, class instance), this error is thrown. It is called from decision(), the public API for emitting decisions from rule handlers.

Source

Thrown at mastracode/factory/src/rules/validation.ts:209

    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 } {
  return {
    idempotencyKey: boundedString(value.idempotencyKey, 'Factory decision idempotencyKey', MAX_IDEMPOTENCY_KEY_LENGTH),
  };
}

export function validateFactoryRuleDecision(value: unknown, causalDepth = 0): FactoryRuleDecision {
  if (causalDepth > MAX_FACTORY_RULE_CAUSAL_DEPTH) {
    throw new FactoryRuleValidationError('Factory rule causal depth exceeded.');
  }
  if (!isPlainObject(value)) throw new FactoryRuleValidationError('Factory rule decision must be an object.');
  const type = value.type;
  if (typeof type !== 'string') throw new FactoryRuleValidationError('Factory rule decision type is required.');

  switch (type) {
    case 'reject': {
      assertExactKeys(value, ['type', 'code', 'reason'], 'Factory reject decision');
      return {
        type,
        code: enumValue(value.code, REJECTION_CODES, 'Factory rejection code'),
        reason: boundedString(value.reason, 'Factory rejection reason', MAX_REASON_LENGTH),
      };
    }
    case 'transition': {
      assertExactKeys(
        value,
        ['type', 'idempotencyKey', 'board', 'stage', 'message', 'reenter'],
        'Factory transition decision',
      );

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Pass a decision object literal like { type: 'transition', idempotencyKey, board, stage }
  2. Check the variable passed to decision() is defined and is an object at the call site
  3. Await any promises before constructing the decision object
  4. Log the value with console.log(typeof v, v) right before calling decision()

Example fix

// before
return decision(result?.decision) // undefined when result is empty
// after
if (!result?.decision) return; // or throw a domain error
return decision(result.decision)
Defensive patterns

Strategy: type-guard

Validate before calling

function assertDecisionInput(v) {
  if (v === null || typeof v !== 'object' || Array.isArray(v)) {
    throw new TypeError('decision() requires a plain object with a type field');
  }
}

Type guard

const isDecisionInput = (v): v is Record<string, unknown> =>
  typeof v === 'object' && v !== null && !Array.isArray(v);

Try / catch

try {
  return decision(value);
} catch (e) {
  if (e instanceof FactoryRuleValidationError && e.message === 'Factory rule decision must be an object.') {
    logger.error('decision() got non-object', { value });
    return null; // or a reject decision with a safe default
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling decision(null), decision(undefined), decision([type, code]), decision('reject'), or passing a Promise/class instance instead of the decision object literal.

Common situations: An onEvent handler accidentally returning/awaiting into decision() a non-object (e.g. awaiting a fetch result before wrapping it); a typo passing the wrong variable; destructuring mistakes producing undefined.

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/966970eb60cf0cd1. Report an issue: GitHub.