{"record":{"id":"e7f1bc2d10b1e398","repo":"mastra-ai/mastra","slug":"github-pull-requests-require-an-owner-repository-s","errorCode":null,"errorMessage":"GitHub pull requests require an owner/repository source.","messagePattern":"GitHub pull requests require an owner/repository source\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/integrations/github/integration.ts","lineNumber":712,"sourceCode":"    const octokit = this.getInstallationOctokit(installationId);\n    try {\n      const { data } = await octokit.issues.createComment({\n        owner: parts.owner,\n        repo: parts.repo,\n        issue_number: issueNumber,\n        body: input.body,\n      });\n      return { id: String(data.id), url: data.html_url };\n    } catch (err) {\n      if (isNotFoundError(err)) return null;\n      throw err;\n    }\n  }\n\n  #repositoryClient(connection: IntegrationConnection, sourceId: string) {\n    const installationId = getGithubInstallationId(connection);\n    const parts = splitRepoFullName(sourceId);\n    if (!parts) throw new Error('GitHub pull requests require an owner/repository source.');\n    return { octokit: this.getInstallationOctokit(installationId), parts };\n  }\n\n  async #listPullRequests(input: InputOf<'listPullRequests'>) {\n    const { octokit, parts } = this.#repositoryClient(input.connection, input.sourceId);\n    const page = parsePositiveCursor(input.cursor);\n    const response = await octokit.pulls.list({\n      ...parts,\n      state: input.state ?? 'open',\n      per_page: LIST_PAGE_SIZE,\n      page,\n    });\n    const pullRequests = response.data\n      .filter(pr => input.includeDrafts !== false || !pr.draft)\n      .map(pr => parsePullRequest(pr));\n    return {\n      pullRequests,\n      nextCursor: response.data.length === LIST_PAGE_SIZE ? String(page + 1) : null,","sourceCodeStart":694,"sourceCodeEnd":730,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/integrations/github/integration.ts#L694-L730","documentation":"#repositoryClient parses the sourceId into { owner, repo } via splitRepoFullName and throws when parsing fails, because GitHub pull-request operations require a well-formed owner/repository full name to build the Octokit request.","triggerScenarios":"Calling listPullRequests (or any PR operation routed through #repositoryClient) with a sourceId that is not in 'owner/repo' format — e.g. a bare repo name, a URL, a numeric repo id, or an empty string.","commonSituations":"Passing a database record id instead of the GitHub full name; users typing 'my-repo' without the owner; storing a clone URL in sourceId; renaming a repository and keeping a stale slug.","solutions":["Pass sourceId as the GitHub full name 'owner/repo'","Normalize stored source identifiers to owner/repo when creating connections","Use splitRepoFullName (or an equivalent check) on input before calling the integration to give a better upstream error","Update stale connections after repository transfers/renames"],"exampleFix":"// before\nawait gh.listPullRequests({ connection, sourceId: 'my-repo' });\n// after\nawait gh.listPullRequests({ connection, sourceId: 'acme/my-repo' });","handlingStrategy":"validation","validationCode":"const parts = sourceId.split('/');\nif (parts.length !== 2 || !parts[0] || !parts[1]) {\n  throw new Error(`sourceId must be 'owner/repo', got: ${sourceId}`);\n}","typeGuard":"function isRepoFullName(s: string): s is `${string}/${string}` {\n  const [owner, repo, ...rest] = s.split('/');\n  return rest.length === 0 && Boolean(owner && repo);\n}","tryCatchPattern":"try {\n  await gh.listPullRequests({ connection, sourceId });\n} catch (e) {\n  if (e.message === 'GitHub pull requests require an owner/repository source.') {\n    // fix the stored source identifier for this connection\n  } else throw e;\n}","preventionTips":["Store GitHub sources canonically as 'owner/repo' at connection creation time","Normalize or reject URLs/ids before saving sourceId","Re-validate stored sourceIds after repository renames or transfers"],"tags":["github","validation","input"],"backgroundTag":"invalid-identifier-format","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}