mastra-ai/mastra · error · FactoryRuleValidationError
Factory linked work item URL is invalid.
Error message
Factory linked work item URL is invalid.
What it means
validateFactoryRuleDecision validates a 'link' (linked work item) decision before it is committed. When the decision carries a url that is not null, it must be a string, at most MAX_URL_LENGTH characters, and start with http:// or https://. Anything else (wrong type, too long, or a non-http scheme like ftp:// or a bare hostname) throws this error so malformed URLs never reach the factory dispatcher.
Source
Thrown at mastracode/factory/src/rules/validation.ts:263
}
return {
type,
...commonCommitFields(value),
board: enumValue(value.board, FACTORY_RULE_BOARDS, 'Factory transition board'),
stage: enumValue(value.stage, FACTORY_RULE_STAGES, 'Factory transition stage'),
...(message ? { message } : {}),
...(value.reenter === true ? { reenter: true } : {}),
};
}
case 'upsertLinkedWorkItem': {
assertExactKeys(
value,
['type', 'idempotencyKey', 'board', 'source', 'sourceKey', 'title', 'url', 'stage', 'metadata'],
'Factory linked work item decision',
);
const url = value.url;
if (url !== null && (typeof url !== 'string' || url.length > MAX_URL_LENGTH || !/^https?:\/\//.test(url))) {
throw new FactoryRuleValidationError('Factory linked work item URL is invalid.');
}
const metadata = sanitizeMetadata(value.metadata);
return {
type,
...commonCommitFields(value),
board: enumValue(value.board, FACTORY_RULE_BOARDS, 'Factory linked work item board'),
source: enumValue(value.source, WORK_ITEM_SOURCES, 'Factory linked work item source'),
sourceKey: boundedString(value.sourceKey, 'Factory linked work item sourceKey', MAX_SOURCE_KEY_LENGTH),
title: boundedString(value.title, 'Factory linked work item title', MAX_TITLE_LENGTH),
url,
stage: enumValue(value.stage, FACTORY_RULE_STAGES, 'Factory linked work item stage'),
...(metadata ? { metadata } : {}),
};
}
case 'invokeSkill': {
assertExactKeys(
value,
['type', 'idempotencyKey', 'role', 'skillName', 'prompt', 'arguments', 'precedingMessage', 'cancelInFlight'],View on GitHub (pinned to 75dd419e61)
Solutions
- Ensure the url is a full absolute http(s) URL, e.g. prefix relative paths with the tracker's origin.
- Truncate or regenerate URLs that exceed MAX_URL_LENGTH (strip volatile query/auth parameters).
- Set url explicitly to null if no link is intended, instead of an empty or placeholder string.
- Validate the URL with the same regex/length checks in your rule code before emitting the decision.
Example fix
// before
url: issue.webUrl ?? '' // '' or relative path fails the http(s) check
// after
url: issue.webUrl?.startsWith('http') ? issue.webUrl : `https://tracker.example.com${issue.webUrl ?? ''}` || null Defensive patterns
Strategy: validation
Validate before calling
function isValidDecisionUrl(url) {
const MAX_URL_LENGTH = 2048; // match library cap
return url === null || (typeof url === 'string' && url.length <= MAX_URL_LENGTH && /^https?:\/\//.test(url));
}
if (!isValidDecisionUrl(decision.url)) throw new Error('Invalid linked work item URL'); Type guard
function isHttpUrl(u: unknown): u is string {
return typeof u === 'string' && u.length <= 2048 && /^https?:\/\//.test(u);
} Try / catch
try {
commitDecision(decision);
} catch (e) {
if (e instanceof FactoryRuleValidationError && /URL is invalid/.test(e.message)) {
logger.warn('Dropping decision with invalid URL', { url: decision.url });
return null;
}
throw e;
} Prevention
- Always normalize tracker URLs to absolute http(s) form before emitting decisions.
- Validate with the same /^https?:\/\// regex and length cap used by the library.
- Use null (not '') to express 'no link'.
- Strip long-lived query/auth params that inflate URL length.
When it happens
Trigger: Committing a factory rule decision of type linking a work item where value.url is a non-string (e.g. a number or object), exceeds MAX_URL_LENGTH, or does not match /^https?:\/\// (e.g. 'www.example.com/item', 'ftp://host/x', or 'javascript:...' URIs).
Common situations: Rule templates interpolating a board item URL from an external tracker that returns relative paths or custom schemes; hand-written rules pasting a short hostname without the scheme; overly long signed URLs from enterprise trackers (e.g. Azure DevOps/Jira with long tokens in query strings) exceeding the length cap.
Related errors
- Invalid route path: "${path}". Path cannot contain '..', '?'
- Factory rule version is required.
- ${label} contains an unsupported field.
- Rule metadata is too large.
- ${label} must be an object.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/22095446b2f66a81.
Report an issue: GitHub.