{"record":{"id":"22095446b2f66a81","repo":"mastra-ai/mastra","slug":"factory-linked-work-item-url-is-invalid","errorCode":null,"errorMessage":"Factory linked work item URL is invalid.","messagePattern":"Factory linked work item URL is invalid\\.","errorType":"validation","errorClass":"FactoryRuleValidationError","httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/rules/validation.ts","lineNumber":263,"sourceCode":"      }\n      return {\n        type,\n        ...commonCommitFields(value),\n        board: enumValue(value.board, FACTORY_RULE_BOARDS, 'Factory transition board'),\n        stage: enumValue(value.stage, FACTORY_RULE_STAGES, 'Factory transition stage'),\n        ...(message ? { message } : {}),\n        ...(value.reenter === true ? { reenter: true } : {}),\n      };\n    }\n    case 'upsertLinkedWorkItem': {\n      assertExactKeys(\n        value,\n        ['type', 'idempotencyKey', 'board', 'source', 'sourceKey', 'title', 'url', 'stage', 'metadata'],\n        'Factory linked work item decision',\n      );\n      const url = value.url;\n      if (url !== null && (typeof url !== 'string' || url.length > MAX_URL_LENGTH || !/^https?:\\/\\//.test(url))) {\n        throw new FactoryRuleValidationError('Factory linked work item URL is invalid.');\n      }\n      const metadata = sanitizeMetadata(value.metadata);\n      return {\n        type,\n        ...commonCommitFields(value),\n        board: enumValue(value.board, FACTORY_RULE_BOARDS, 'Factory linked work item board'),\n        source: enumValue(value.source, WORK_ITEM_SOURCES, 'Factory linked work item source'),\n        sourceKey: boundedString(value.sourceKey, 'Factory linked work item sourceKey', MAX_SOURCE_KEY_LENGTH),\n        title: boundedString(value.title, 'Factory linked work item title', MAX_TITLE_LENGTH),\n        url,\n        stage: enumValue(value.stage, FACTORY_RULE_STAGES, 'Factory linked work item stage'),\n        ...(metadata ? { metadata } : {}),\n      };\n    }\n    case 'invokeSkill': {\n      assertExactKeys(\n        value,\n        ['type', 'idempotencyKey', 'role', 'skillName', 'prompt', 'arguments', 'precedingMessage', 'cancelInFlight'],","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/rules/validation.ts#L245-L281","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nurl: issue.webUrl ?? '' // '' or relative path fails the http(s) check\n// after\nurl: issue.webUrl?.startsWith('http') ? issue.webUrl : `https://tracker.example.com${issue.webUrl ?? ''}` || null","handlingStrategy":"validation","validationCode":"function isValidDecisionUrl(url) {\n  const MAX_URL_LENGTH = 2048; // match library cap\n  return url === null || (typeof url === 'string' && url.length <= MAX_URL_LENGTH && /^https?:\\/\\//.test(url));\n}\nif (!isValidDecisionUrl(decision.url)) throw new Error('Invalid linked work item URL');","typeGuard":"function isHttpUrl(u: unknown): u is string {\n  return typeof u === 'string' && u.length <= 2048 && /^https?:\\/\\//.test(u);\n}","tryCatchPattern":"try {\n  commitDecision(decision);\n} catch (e) {\n  if (e instanceof FactoryRuleValidationError && /URL is invalid/.test(e.message)) {\n    logger.warn('Dropping decision with invalid URL', { url: decision.url });\n    return null;\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["validation","url","factory-rules"],"backgroundTag":"invalid-url-format","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}