{"record":{"id":"492a4b01d280aacc","repo":"mastra-ai/mastra","slug":"pull-request-must-belong-to-expectedrepo","errorCode":null,"errorMessage":"Pull request must belong to ${expectedRepo}.","messagePattern":"Pull request must belong to (.+?)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/integrations/github/session-subscriptions.ts","lineNumber":78,"sourceCode":"  }\n}\n\ninterface SessionTarget {\n  context: AgentControllerRequestContext<RepositorySessionState>;\n  projectRepository: ProjectRepository;\n  connection: ProjectSourceControlConnection;\n  installation: SourceControlInstallation;\n  repository: SourceControlRepository;\n  orgId: string;\n  userId: string;\n}\n\nfunction parsePullRequest(value: number | string, expectedRepo: string): number {\n  if (typeof value === 'number') return value;\n  if (/^\\d+$/.test(value)) return Number(value);\n  const match = value.match(/^https:\\/\\/github\\.com\\/([^/]+\\/[^/]+)\\/pull\\/(\\d+)\\/?$/i);\n  if (!match || match[1]!.toLowerCase() !== expectedRepo.toLowerCase()) {\n    throw new Error(`Pull request must belong to ${expectedRepo}.`);\n  }\n  return Number(match[2]);\n}\n\n/**\n * Whether the current request comes from a session that GitHub subscriptions\n * can ever apply to: an authenticated org user on a GitHub-project session\n * with an active thread. Mirrors the gate in `resolveSessionTarget` without\n * throwing, for passive callers that should no-op instead of erroring.\n */\nfunction isGithubProjectSession(requestContext: RequestContext): boolean {\n  const context = requestContext.get('controller') as AgentControllerRequestContext<RepositorySessionState> | undefined;\n  return Boolean(\n    context?.threadId &&\n    context.getState().projectRepositoryId &&\n    sessionOrgId(requestContext) &&\n    sessionUserId(requestContext),\n  );","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/integrations/github/session-subscriptions.ts#L60-L96","documentation":"parsePullRequest accepts a PR number, digit string, or full GitHub PR URL. If a URL-like or non-numeric string is given that either is not a valid github.com pull URL or points to a repository other than the expected one (case-insensitive comparison of owner/repo), this error is thrown. It protects subscriptions from targeting PRs in a different repo.","triggerScenarios":"Passing a PR URL whose owner/repo does not match expectedRepo, or a malformed string that is neither a number nor a matching https://github.com/{owner}/{repo}/pull/{n} URL.","commonSituations":"Users pasting a PR URL from a fork or a different organization; using an SSH-style or gitlab-style URL; trailing path segments like /pull/123/files breaking the strict regex.","solutions":["Pass a plain PR number (or digit string) instead of a URL when the repo is already known","Use a canonical https://github.com/{owner}/{repo}/pull/{number} URL matching the expected repository","Strip extra path suffixes (e.g. /files, #discussion anchors) from the URL before passing it","Verify the owner/repo segment in the URL equals the active project repository slug"],"exampleFix":"// before\nsubscribe({ pullRequest: 'https://github.com/other-org/repo/pull/42/files' });\n// after\nsubscribe({ pullRequest: 42 });","handlingStrategy":"validation","validationCode":"function isValidPullRequestRef(value: number | string, expectedRepo: string): boolean {\n  if (typeof value === 'number') return true;\n  if (/^\\d+$/.test(value)) return true;\n  const m = value.match(/^https:\\/\\/github\\.com\\/([^/]+\\/[^/]+)\\/pull\\/(\\d+)\\/?$/i);\n  return !!m && m[1].toLowerCase() === expectedRepo.toLowerCase();\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass plain PR numbers instead of URLs when possible","Normalize pasted URLs: strip /files, #anchors and trailing slashes","Confirm the URL's owner/repo matches the active project repository"],"tags":["validation","github","pull-request","url-parsing"],"backgroundTag":"invalid-pull-request-reference","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}