{"record":{"id":"4b868a9d448a73d4","repo":"mastra-ai/mastra","slug":"factory-rules-must-be-an-object","errorCode":null,"errorMessage":"Factory rules must be an object.","messagePattern":"Factory rules must be an object\\.","errorType":"validation","errorClass":"FactoryRuleValidationError","httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/rules/validation.ts","lineNumber":161,"sourceCode":"  if (!isPlainObject(rules)) throw new FactoryRuleValidationError(`${label} must be an object.`);\n  for (const [stage, sources] of Object.entries(rules)) {\n    enumValue(stage, FACTORY_RULE_STAGES, `${label} stage`);\n    if (!isPlainObject(sources)) throw new FactoryRuleValidationError(`${label}.${stage} must be an object.`);\n    for (const [source, leaf] of Object.entries(sources)) {\n      enumValue(source, FACTORY_RULE_SOURCES, `${label}.${stage} source`);\n      if (!isPlainObject(leaf)) throw new FactoryRuleValidationError(`${label}.${stage}.${source} must be an object.`);\n      assertExactKeys(leaf, ['onEnter', 'onExit'], `${label}.${stage}.${source}`);\n      for (const handler of Object.values(leaf)) {\n        if (handler !== undefined && typeof handler !== 'function') {\n          throw new FactoryRuleValidationError(`${label}.${stage}.${source} handlers must be functions.`);\n        }\n      }\n    }\n  }\n}\n\nexport function assertFactoryRules(rules: unknown): asserts rules is FactoryRules {\n  if (!isPlainObject(rules)) throw new FactoryRuleValidationError('Factory rules must be an object.');\n  assertExactKeys(rules, ['version', 'work', 'review', 'tools', 'github', 'linear'], 'Factory rules');\n  boundedString(rules.version, 'Factory rule version', MAX_VERSION_LENGTH);\n  validateBoardRules(rules.work, 'Factory rules.work');\n  validateBoardRules(rules.review, 'Factory rules.review');\n\n  if (!isPlainObject(rules.tools)) throw new FactoryRuleValidationError('Factory rules.tools must be an object.');\n  for (const [toolName, leaf] of Object.entries(rules.tools)) {\n    boundedString(toolName, 'Factory tool name', 128, IDENTIFIER_RE);\n    if (!isPlainObject(leaf))\n      throw new FactoryRuleValidationError(`Factory rules.tools.${toolName} must be an object.`);\n    assertExactKeys(leaf, ['onResult'], `Factory rules.tools.${toolName}`);\n    if (leaf.onResult !== undefined && typeof leaf.onResult !== 'function') {\n      throw new FactoryRuleValidationError(`Factory rules.tools.${toolName}.onResult must be a function.`);\n    }\n  }\n\n  if (!isPlainObject(rules.github)) throw new FactoryRuleValidationError('Factory rules.github must be an object.');\n  for (const [event, leaf] of Object.entries(rules.github)) {","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/rules/validation.ts#L143-L179","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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() })."],"exampleFix":"// before\nassertFactoryRules(loadRulesConfig()); // may return null\n// after\nconst config = loadRulesConfig();\nassertFactoryRules(config ?? { version: '1', work: {}, review: {}, tools: {}, github: {}, linear: {} });","handlingStrategy":"try-catch","validationCode":"function hasCompleteRulesShape(v) {\n  return v !== null && typeof v === 'object' && !Array.isArray(v) &&\n    ['version', 'work', 'review', 'tools', 'github', 'linear'].every(k => k in v);\n}\nif (!hasCompleteRulesShape(config?.rules)) throw new Error('rules config missing or malformed');","typeGuard":"const isFactoryRulesLike = (v: unknown): v is Record<string, unknown> =>\n  typeof v === 'object' && v !== null && !Array.isArray(v) &&\n  (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null);","tryCatchPattern":"try {\n  assertFactoryRules(config.rules);\n} catch (e) {\n  if (e.name === 'FactoryRuleValidationError' && e.code === 'invalid_factory_rule') {\n    console.error('Rules config is not a valid object; falling back to defaults.');\n    return defaultFactoryRules();\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["validation","null-check","factory-rules","config"],"backgroundTag":"schema-validation-failed","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}