{"record":{"id":"f5756a43c8e9b755","repo":"mastra-ai/mastra","slug":"repository-access-did-not-include-a-bearer-token-f","errorCode":null,"errorMessage":"Repository access did not include a bearer token for the Factory session.","messagePattern":"Repository access did not include a bearer token for the Factory session\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/integrations/github/session-subscriptions.ts","lineNumber":216,"sourceCode":"  // `GH_TOKEN` feeds the `gh` CLI, so a configured org PAT wins over a minted\n  // installation token (which 403s on integration-restricted endpoints). The\n  // workspace records which PAT kind the sandbox was provisioned with, so a\n  // review-board sandbox keeps its reviewer token on refresh.\n  const pat = await getGithubPat(\n    () => github.integrationStorage,\n    target.orgId,\n    getRegisteredGithubPatKind(requestContext),\n  );\n  if (pat) {\n    injectGithubToken(requestContext, pat);\n    return;\n  }\n  const access = await github.versionControl.getRepositoryAccess({\n    orgId: target.orgId,\n    repositoryId: target.repository.id,\n  });\n  const token = access.authorization?.token;\n  if (!token) throw new Error('Repository access did not include a bearer token for the Factory session.');\n  injectGithubToken(requestContext, token);\n}\n\nexport function createGithubSubscriptionTools(requestContext: RequestContext, github: GithubIntegration) {\n  if (!isGithubProjectSession(requestContext)) return {};\n\n  return {\n    github_refresh_token: createTool({\n      id: 'github_refresh_token',\n      description:\n        'Refresh GitHub CLI authentication in the active Factory sandbox. Use this after a gh command fails because authentication is expired, invalid, or missing. It installs a fresh GH_TOKEN for subsequent sandbox commands. After this tool succeeds, retry the failed gh command. Takes no arguments and never returns the token.',\n      inputSchema: z.object({}),\n      execute: async () => {\n        await refreshGithubToken(requestContext, github);\n        return { refreshed: true };\n      },\n    }),\n    github_upsert_factory_triage_comment: createTool({","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/integrations/github/session-subscriptions.ts#L198-L234","documentation":"refreshGithubToken fetches repository access from the version-control backend and expects the response to carry an authorization token that can be injected into the Factory session's GitHub client. If getRepositoryAccess succeeds but returns no bearer token, the session cannot authenticate to GitHub, so the code throws rather than proceeding with an unauthenticated client. This indicates the token grant layer did not produce credentials for this org/repository pair.","triggerScenarios":"Calling refreshGithubToken (wired into the GitHub subscription tools) when github.versionControl.getRepositoryAccess({ orgId, repositoryId }) resolves with access.authorization undefined or access.authorization.token empty.","commonSituations":"The GitHub App installation lost its token grant ( revoked permissions, expired installation); the org has no credentials registered for that repository; backend/auth service returns a 200 with an empty authorization payload; the repository was moved or deleted so access resolution silently returns no auth.","solutions":["Re-authorize/re-install the GitHub App for the org so a fresh repository access token can be issued","Check the credential/token store backing getRepositoryAccess for the given orgId+repositoryId and ensure a token exists and is unexpired","Confirm the repository ID belongs to the org passed in — a mismatched org/repository pair can resolve access without authorization","Add a token-presence check before calling refreshGithubToken and skip/re-queue token refresh when none is available"],"exampleFix":"// before\nconst access = await github.versionControl.getRepositoryAccess({ orgId, repositoryId });\nif (!access.authorization?.token) throw new Error('no token');\n// after\nconst access = await github.versionControl.getRepositoryAccess({ orgId, repositoryId });\nif (!access.authorization?.token) {\n  logger.warn('No repository token for %s/%s — re-authorizing GitHub installation', orgId, repositoryId);\n  await reconnectGithubInstallation(orgId);\n  return;\n}","handlingStrategy":"validation","validationCode":"const access = await github.versionControl.getRepositoryAccess({ orgId, repositoryId });\nif (!access.authorization?.token) {\n  throw new Error('No repository token available — re-authorize the GitHub installation.');\n}","typeGuard":"function hasBearerToken(access: unknown): access is { authorization: { token: string } } {\n  return typeof access === 'object' && access !== null &&\n    'authorization' in access && typeof (access as any).authorization?.token === 'string' &&\n    (access as any).authorization.token.length > 0;\n}","tryCatchPattern":"try {\n  await refreshGithubToken(requestContext, github);\n} catch (err) {\n  if ((err as Error).message.includes('did not include a bearer token')) {\n    await reconnectGithubInstallation(target.orgId);\n    return;\n  }\n  throw err;\n}","preventionTips":["Monitor GitHub App installation/authorization revocation events and re-auth proactively","Verify org+repository pairing before requesting repository access","Check token expiry in the credential store and refresh before it lapses","Log empty authorization payloads from getRepositoryAccess to catch backend regressions"],"tags":["github","authentication","token"],"backgroundTag":"missing-auth-token","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}