mastra-ai/mastra · error · Error
GitHub integration is required to retire webhook subscriptio
Error message
GitHub integration is required to retire webhook subscriptions.
What it means
The retireSubscription dependency in dispatchGithubWebhook defaults to retirePullRequestSubscription, which writes through dependencies.github.integrationStorage. When the GitHub integration dependency is not provided and no custom retireSubscription override is injected, the closure throws so closed/merged PR events cannot silently leave subscriptions unretired.
Source
Thrown at mastracode/factory/src/integrations/github/webhook.ts:541
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) {
try {
const session = await resolveSubscriptionSession(dependencies.controller, subscription, dependencies.github);
// No session means this deployment does not hold the subscribed thread.
// That is not a delivery failure, so it must not be retried or counted as
// one; the subscription is left untouched because the thread may exist
// wherever the subscription was created.
if (!session) {
skipped += 1;
dependencies.onTargetSkipped?.(subscription);
continue;View on GitHub (pinned to 75dd419e61)
Solutions
- Pass dependencies.github to dispatchGithubWebhook so retirement can write to integrationStorage
- Provide a custom dependencies.retireSubscription override when operating without the full integration
- Require dependencies.github in the type signature so omissions are caught at compile time
- Add an early validation in dispatchGithubWebhook that either github or both list/retire overrides exist
Example fix
// before
await dispatchGithubWebhook(prNotification, { controller });
// after
await dispatchGithubWebhook(prNotification, {
controller,
github: { integrationStorage },
retireSubscription: (id, status) => retirePullRequestSubscription(id, status, integrationStorage),
}); Defensive patterns
Strategy: validation
Validate before calling
if (!dependencies.github && !dependencies.retireSubscription) {
throw new Error('dispatchGithubWebhook requires dependencies.github or a retireSubscription override.');
} Type guard
function canRetireSubscriptions(deps: { github?: unknown; retireSubscription?: unknown }): boolean {
return typeof deps.retireSubscription === '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 retire webhook subscriptions')) {
logger.error('Cannot retire PR subscriptions: GitHub integration missing from dispatcher deps');
return { delivered: 0, failed: 0, skipped: 0 };
}
throw err;
} Prevention
- Inject the GitHub integration wherever the webhook dispatcher is constructed
- Test the closed/merged PR paths specifically — they are the ones that hit retireSubscription
- Fail fast at wiring time rather than at first webhook delivery
- Centralize dependency construction for webhooks in one module
When it happens
Trigger: A PR 'closed' or 'merged' webhook dispatches and dispatchGithubWebhook needs to retire matching subscriptions, but dependencies.github is undefined and no custom dependencies.retireSubscription was supplied.
Common situations: Same wiring omission as the listSubscriptions sibling error: entry point constructed without the GitHub integration; tests with partial dependencies that pass listSubscriptions but exercise the retire path; refactors that made the dependency optional without updating all call sites.
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 load webhook subscriptions
- GitHub subscription ${subscription.id} is missing its sessio
- GitHub subscription ${subscription.id} has no Factory sessio
- Session ${sessionId} did not bind thread ${threadId}.
- WorkOS webhook secret is required. Provide it in options or
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/83270e071bca5e81.
Report an issue: GitHub.