mastra-ai/mastra · error · Error
GitHub integration is required to load webhook…
Error message
GitHub integration is required to load webhook subscriptions.
What it means
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.
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
Example fix
// before
await dispatchGithubWebhook(notification, { controller, sessions });
// after
await dispatchGithubWebhook(notification, {
controller,
sessions,
github: { integrationStorage, sourceControlStorage },
}); Defensive patterns
Strategy: validation
Validate before calling
type WebhookDeps = { github?: GithubWebhookDispatchIntegration; listSubscriptions?: ... };
function assertGithubDeps(deps: WebhookDeps): asserts deps is WebhookDeps & { github: GithubWebhookDispatchIntegration } {
if (!deps.github && !deps.listSubscriptions) {
throw new Error('dispatchGithubWebhook requires dependencies.github or a listSubscriptions override.');
}
} Type guard
function canListSubscriptions(deps: { github?: unknown; listSubscriptions?: unknown }): boolean {
return typeof deps.listSubscriptions === 'function' || typeof deps.github === 'object' && deps.github !== null;
} Try / catch
try {
await dispatchGithubWebhook(notification, deps);
} catch (err) {
if ((err as Error).message.includes('required to load webhook subscriptions')) {
logger.error('Webhook dispatcher misconfigured: pass dependencies.github');
return { delivered: 0, failed: 0, skipped: 0 };
}
throw err;
} Prevention
- 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
When it happens
Trigger: Calling dispatchGithubWebhook without providing dependencies.github (or a custom dependencies.listSubscriptions) when a pull-request notification needs subscriptions listed.
Common situations: 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.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
Related errors
- GitHub integration is required to retire webhook…
- GitHub subscription has no Factory session to run as.
- GitHub subscription is missing its session binding.
- Session did not bind thread .
- A multi-line review comment requires both startLine and…
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/e96686e1822c062f.
Report an issue: GitHub.
Appendix: source
Thrown at mastracode/factory/src/integrations/github/webhook.ts:531
const notification = classifyGithubWebhook(parsed);
if (!notification) return { delivered: 0, failed: 0, skipped: 0, ignored: true };
const isAuthorizedSender =
dependencies.isAuthorizedSender ??
((n: GithubWebhookNotification) => isAuthorizedGithubSender(n, dependencies.github));
if (!(await isAuthorizedSender(notification))) {
dependencies.onSenderRejected?.(notification);
return { delivered: 0, failed: 0, skipped: 0, ignored: true };
}
const target = {
installationExternalId: notification.metadata.installationId.toString(),
repositoryExternalId: notification.metadata.repositoryId.toString(),
changeRequestId: notification.metadata.pullRequestNumber.toString(),
};
const listSubscriptions =
dependencies.listSubscriptions ??
((subscriptionTarget: GithubWebhookPullRequestTarget, options?: { includeTerminal?: boolean }) => {
if (!dependencies.github) throw new Error('GitHub integration is required to load webhook subscriptions.');
return listPullRequestSubscriptionsForWebhook(
subscriptionTarget,
options,
dependencies.github.integrationStorage,
);
});
const retireSubscription =
dependencies.retireSubscription ??
((id: string, status: 'open' | 'closed' | 'merged') => {
if (!dependencies.github) throw new Error('GitHub integration is required to retire webhook subscriptions.');
return retirePullRequestSubscription(id, status, dependencies.github.integrationStorage);
});
const subscriptions = await listSubscriptions(target, { includeTerminal: notification.action === 'reopened' });
let delivered = 0;
let failed = 0;
let skipped = 0;
for (const subscription of subscriptions) {View on GitHub (pinned to 75dd419e61)