mastra-ai/mastra · error

Pull request must belong to ${expectedRepo}.

Error message

Pull request must belong to ${expectedRepo}.

What it means

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.

Source

Thrown at mastracode/factory/src/integrations/github/session-subscriptions.ts:78

  }
}

interface SessionTarget {
  context: AgentControllerRequestContext<RepositorySessionState>;
  projectRepository: ProjectRepository;
  connection: ProjectSourceControlConnection;
  installation: SourceControlInstallation;
  repository: SourceControlRepository;
  orgId: string;
  userId: string;
}

function parsePullRequest(value: number | string, expectedRepo: string): number {
  if (typeof value === 'number') return value;
  if (/^\d+$/.test(value)) return Number(value);
  const match = value.match(/^https:\/\/github\.com\/([^/]+\/[^/]+)\/pull\/(\d+)\/?$/i);
  if (!match || match[1]!.toLowerCase() !== expectedRepo.toLowerCase()) {
    throw new Error(`Pull request must belong to ${expectedRepo}.`);
  }
  return Number(match[2]);
}

/**
 * Whether the current request comes from a session that GitHub subscriptions
 * can ever apply to: an authenticated org user on a GitHub-project session
 * with an active thread. Mirrors the gate in `resolveSessionTarget` without
 * throwing, for passive callers that should no-op instead of erroring.
 */
function isGithubProjectSession(requestContext: RequestContext): boolean {
  const context = requestContext.get('controller') as AgentControllerRequestContext<RepositorySessionState> | undefined;
  return Boolean(
    context?.threadId &&
    context.getState().projectRepositoryId &&
    sessionOrgId(requestContext) &&
    sessionUserId(requestContext),
  );

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Pass a plain PR number (or digit string) instead of a URL when the repo is already known
  2. Use a canonical https://github.com/{owner}/{repo}/pull/{number} URL matching the expected repository
  3. Strip extra path suffixes (e.g. /files, #discussion anchors) from the URL before passing it
  4. Verify the owner/repo segment in the URL equals the active project repository slug

Example fix

// before
subscribe({ pullRequest: 'https://github.com/other-org/repo/pull/42/files' });
// after
subscribe({ pullRequest: 42 });
Defensive patterns

Strategy: validation

Validate before calling

function isValidPullRequestRef(value: number | string, expectedRepo: string): boolean {
  if (typeof value === 'number') return true;
  if (/^\d+$/.test(value)) return true;
  const m = value.match(/^https:\/\/github\.com\/([^/]+\/[^/]+)\/pull\/(\d+)\/?$/i);
  return !!m && m[1].toLowerCase() === expectedRepo.toLowerCase();
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/492a4b01d280aacc. Report an issue: GitHub.