{"record":{"id":"e96686e1822c062f","repo":"mastra-ai/mastra","slug":"github-integration-is-required-to-load-webhook-sub","errorCode":null,"errorMessage":"GitHub integration is required to load webhook subscriptions.","messagePattern":"GitHub integration is required to load webhook subscriptions\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/integrations/github/webhook.ts","lineNumber":531,"sourceCode":"  const notification = classifyGithubWebhook(parsed);\n  if (!notification) return { delivered: 0, failed: 0, skipped: 0, ignored: true };\n  const isAuthorizedSender =\n    dependencies.isAuthorizedSender ??\n    ((n: GithubWebhookNotification) => isAuthorizedGithubSender(n, dependencies.github));\n  if (!(await isAuthorizedSender(notification))) {\n    dependencies.onSenderRejected?.(notification);\n    return { delivered: 0, failed: 0, skipped: 0, ignored: true };\n  }\n\n  const target = {\n    installationExternalId: notification.metadata.installationId.toString(),\n    repositoryExternalId: notification.metadata.repositoryId.toString(),\n    changeRequestId: notification.metadata.pullRequestNumber.toString(),\n  };\n  const listSubscriptions =\n    dependencies.listSubscriptions ??\n    ((subscriptionTarget: GithubWebhookPullRequestTarget, options?: { includeTerminal?: boolean }) => {\n      if (!dependencies.github) throw new Error('GitHub integration is required to load webhook subscriptions.');\n      return listPullRequestSubscriptionsForWebhook(\n        subscriptionTarget,\n        options,\n        dependencies.github.integrationStorage,\n      );\n    });\n  const retireSubscription =\n    dependencies.retireSubscription ??\n    ((id: string, status: 'open' | 'closed' | 'merged') => {\n      if (!dependencies.github) throw new Error('GitHub integration is required to retire webhook subscriptions.');\n      return retirePullRequestSubscription(id, status, dependencies.github.integrationStorage);\n    });\n  const subscriptions = await listSubscriptions(target, { includeTerminal: notification.action === 'reopened' });\n  let delivered = 0;\n  let failed = 0;\n  let skipped = 0;\n\n  for (const subscription of subscriptions) {","sourceCodeStart":513,"sourceCodeEnd":549,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/integrations/github/webhook.ts#L513-L549","documentation":"dispatchGithubWebhook lists PR subscriptions via an injectable listSubscriptions dependency; when no override is supplied it falls back to listPullRequestSubscriptionsForWebhook, which requires dependencies.github.integrationStorage. If dependencies.github is absent, there is no storage to read subscriptions from, so the lazily-built closure throws with a clear message instead of an opaque undefined-access error.","triggerScenarios":"Calling dispatchGithubWebhook without providing dependencies.github (or a custom dependencies.listSubscriptions) when a pull-request notification needs subscriptions listed.","commonSituations":"Wiring the webhook dispatcher in a new entry point (server route, queue consumer) and forgetting to pass the GitHub integration dependency; test harness constructs partial dependencies; a refactor made the github dependency optional in the type but required at runtime.","solutions":["Pass dependencies.github when constructing/calling dispatchGithubWebhook so integrationStorage is available","Supply a custom dependencies.listSubscriptions override if running without the full GitHub integration","Make dependencies.github a required constructor/parameter type so TypeScript catches the omission at compile time","Add an upfront guard at the top of dispatchGithubWebhook that fails fast with a configuration error listing the missing dependency"],"exampleFix":"// before\nawait dispatchGithubWebhook(notification, { controller, sessions });\n// after\nawait dispatchGithubWebhook(notification, {\n  controller,\n  sessions,\n  github: { integrationStorage, sourceControlStorage },\n});","handlingStrategy":"validation","validationCode":"type WebhookDeps = { github?: GithubWebhookDispatchIntegration; listSubscriptions?: ... };\nfunction assertGithubDeps(deps: WebhookDeps): asserts deps is WebhookDeps & { github: GithubWebhookDispatchIntegration } {\n  if (!deps.github && !deps.listSubscriptions) {\n    throw new Error('dispatchGithubWebhook requires dependencies.github or a listSubscriptions override.');\n  }\n}","typeGuard":"function canListSubscriptions(deps: { github?: unknown; listSubscriptions?: unknown }): boolean {\n  return typeof deps.listSubscriptions === 'function' || typeof deps.github === 'object' && deps.github !== null;\n}","tryCatchPattern":"try {\n  await dispatchGithubWebhook(notification, deps);\n} catch (err) {\n  if ((err as Error).message.includes('required to load webhook subscriptions')) {\n    logger.error('Webhook dispatcher misconfigured: pass dependencies.github');\n    return { delivered: 0, failed: 0, skipped: 0 };\n  }\n  throw err;\n}","preventionTips":["Make dependencies.github a required field in the dispatcher's dependency type","Add a startup check in the server bootstrap that the GitHub integration is wired into the webhook dispatcher","Cover the webhook entry point with a smoke test that delivers a synthetic PR notification","Keep a single factory function for building webhook dependencies so call sites can't drift"],"tags":["github","webhook","dependency-injection","misconfiguration"],"backgroundTag":"missing-dependency","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}