mastra-ai/mastra · error · FactoryRuleValidationError
Factory rules.tools.${toolName}.onResult must be a function.
Error message
Factory rules.tools.${toolName}.onResult must be a function. What it means
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.
Source
Thrown at mastracode/factory/src/rules/validation.ts:174
}
}
}
export function assertFactoryRules(rules: unknown): asserts rules is FactoryRules {
if (!isPlainObject(rules)) throw new FactoryRuleValidationError('Factory rules must be an object.');
assertExactKeys(rules, ['version', 'work', 'review', 'tools', 'github', 'linear'], 'Factory rules');
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}`);View on GitHub (pinned to 75dd419e61)
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.
Example fix
// before
tools: { 'run-tests': { onResult: enabled && onTestResult } }
// after
tools: { 'run-tests': { onResult: enabled ? onTestResult : undefined } } Defensive patterns
Strategy: validation
Validate before calling
for (const [name, leaf] of Object.entries(rules.tools ?? {})) {
const onResult = leaf?.onResult;
if (onResult !== undefined && typeof onResult !== 'function') {
throw new Error(`tools.${name}.onResult must be a function or omitted`);
}
} Type guard
const hasCallableOnResult = (v: unknown): v is { onResult?: (result: unknown) => unknown } =>
typeof v === 'object' && v !== null &&
((v as { onResult?: unknown }).onResult === undefined ||
typeof (v as { onResult?: unknown }).onResult === 'function'); Try / catch
try {
assertFactoryRules(rules);
} catch (e) {
if (e.name === 'FactoryRuleValidationError' && /onResult must be a function/.test(e.message)) {
console.error(`Resolve handler reference: ${e.message}`); // map stored names back to functions
} else throw e;
} Prevention
- 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.
When it happens
Trigger: rules.tools.<toolName>.onResult assigned a string function name, a Promise, a boolean, a number, or an object during assertFactoryRules.
Common situations: 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.
Related errors
- ${label}.${stage}.${source} handlers must be functions.
- Factory rules.tools.${toolName} must be an object.
- ${label} must be a string.
- ${label} must be an object.
- ${label}.${stage} must be an object.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/c2e926b7ff238bb5.
Report an issue: GitHub.