{"record":{"id":"c2e926b7ff238bb5","repo":"mastra-ai/mastra","slug":"factory-rules-tools-toolname-onresult-must-be-a","errorCode":null,"errorMessage":"Factory rules.tools.${toolName}.onResult must be a function.","messagePattern":"Factory rules\\.tools\\.(.+?)\\.onResult must be a function\\.","errorType":"validation","errorClass":"FactoryRuleValidationError","httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/rules/validation.ts","lineNumber":174,"sourceCode":"    }\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)) {\n    enumValue(event, FACTORY_GITHUB_EVENTS, 'Factory GitHub event');\n    if (!isPlainObject(leaf)) throw new FactoryRuleValidationError(`Factory rules.github.${event} must be an object.`);\n    assertExactKeys(leaf, ['onEvent'], `Factory rules.github.${event}`);\n    if (leaf.onEvent !== undefined && typeof leaf.onEvent !== 'function') {\n      throw new FactoryRuleValidationError(`Factory rules.github.${event}.onEvent must be a function.`);\n    }\n  }\n\n  if (!isPlainObject(rules.linear)) throw new FactoryRuleValidationError('Factory rules.linear must be an object.');\n  for (const [event, leaf] of Object.entries(rules.linear)) {\n    enumValue(event, FACTORY_LINEAR_EVENTS, 'Factory Linear event');\n    if (!isPlainObject(leaf)) throw new FactoryRuleValidationError(`Factory rules.linear.${event} must be an object.`);\n    assertExactKeys(leaf, ['onEvent'], `Factory rules.linear.${event}`);","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/rules/validation.ts#L156-L192","documentation":"A tool rule leaf may only contain an onResult key, and when present it must be a function (undefined is allowed). This error fires when onResult is set to any non-callable value, so the library would be unable to invoke it after a tool run.","triggerScenarios":"rules.tools.<toolName>.onResult assigned a string function name, a Promise, a boolean, a number, or an object during assertFactoryRules.","commonSituations":"Rules round-tripped through JSON so the function became a string; assigning `myFn()` invocation result instead of `myFn`; conditional assignment `onResult: enabled && handler` which yields false when disabled.","solutions":["Assign the function itself: onResult: handler.","Fix conditional assignment to onResult: enabled ? handler : undefined.","If handlers are configured by name, resolve names to functions before validation."],"exampleFix":"// before\ntools: { 'run-tests': { onResult: enabled && onTestResult } }\n// after\ntools: { 'run-tests': { onResult: enabled ? onTestResult : undefined } }","handlingStrategy":"validation","validationCode":"for (const [name, leaf] of Object.entries(rules.tools ?? {})) {\n  const onResult = leaf?.onResult;\n  if (onResult !== undefined && typeof onResult !== 'function') {\n    throw new Error(`tools.${name}.onResult must be a function or omitted`);\n  }\n}","typeGuard":"const hasCallableOnResult = (v: unknown): v is { onResult?: (result: unknown) => unknown } =>\n  typeof v === 'object' && v !== null &&\n  ((v as { onResult?: unknown }).onResult === undefined ||\n   typeof (v as { onResult?: unknown }).onResult === 'function');","tryCatchPattern":"try {\n  assertFactoryRules(rules);\n} catch (e) {\n  if (e.name === 'FactoryRuleValidationError' && /onResult must be a function/.test(e.message)) {\n    console.error(`Resolve handler reference: ${e.message}`); // map stored names back to functions\n  } else throw e;\n}","preventionTips":["Assign function references, never strings, Promises, or boolean short-circuit results.","If handlers come from config, add a resolver step (name -> fn) before validation.","Add a type annotation for onResult so wrong types fail at compile time."],"tags":["validation","handlers","type-mismatch","tools"],"backgroundTag":"handler-not-a-function","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}