mastra-ai/mastra · error
Agent "${agentId}" is already connected to Slack. Disconnect
Error message
Agent "${agentId}" is already connected to Slack. Disconnect first to reconnect. What it means
connect() checks stored installations and refuses to create a second Slack app for an agent whose existing installation record has status 'active'. Reconnecting requires an explicit disconnect, preventing duplicate apps and dangling webhook configurations.
Source
Thrown at channels/slack/src/provider.ts:1116
);
}
console.log(`[Slack] Reusing existing pending installation for "${agentId}"`);
return {
type: 'oauth' as const,
installationId: decrypted.id,
authorizationUrl: decrypted.authorizationUrl,
};
}
} catch {
// Corrupt pending record — delete it and create a fresh one
console.warn(`[Slack] Corrupt pending installation for "${agentId}", replacing it`);
await storage.deleteInstallation(existingRecord.id);
}
}
// If already connected, throw rather than creating a second app
if (existingRecord?.status === 'active') {
throw new Error(`Agent "${agentId}" is already connected to Slack. Disconnect first to reconnect.`);
}
const config = options ?? {};
// Generate unique webhook ID for this installation
const webhookId = crypto.randomUUID();
// Build manifest using the manifest builder (includes proper default scopes)
const appName = config.name ?? agent?.name ?? agentId;
const appDescription = config.description || agent?.getDescription() || 'AI assistant powered by Mastra';
const normalizedCommands = this.#normalizeCommands(config.slashCommands);
let manifest = buildManifest({
name: appName,
description: appDescription,
webhookUrl: `${baseUrl}/slack/events/${webhookId}`,
oauthRedirectUrl: `${baseUrl}/slack/oauth/callback`,
commandsUrl: `${baseUrl}/slack/commands/${webhookId}`,
slashCommands: normalizedCommands.map(cmd => ({View on GitHub (pinned to 75dd419e61)
Solutions
- Call await provider.disconnect(agentId) first, then reconnect.
- If the record is stale (app deleted in Slack), remove the installation record from storage (storage.deleteInstallation(id)) and reconnect.
- Make connect calls idempotent in your boot code by checking connection status before calling connect.
Example fix
// before
await provider.connect('support-agent'); // throws if already active
// after
const status = await provider.getConnectionStatus('support-agent');
if (status !== 'active') {
await provider.connect('support-agent');
} Defensive patterns
Strategy: try-catch
Validate before calling
const existing = await provider.getConnectionStatus?.(agentId);
if (existing === 'active') {
return; // already connected, skip connect
} Try / catch
try {
await provider.connect(agentId);
} catch (err) {
if (err instanceof Error && err.message.includes('already connected to Slack')) {
// idempotent reconnect: disconnect then reconnect, or just ignore
await provider.disconnect(agentId);
await provider.connect(agentId);
} else throw err;
} Prevention
- Make connect-on-boot idempotent by checking installation status first.
- Never blind-retry connect() after a success; track completion state in your own job store.
- Disconnect explicitly when removing or recreating a Slack app for an agent.
When it happens
Trigger: Calling connect(agentIdOrOptions) for an agent whose installation record in storage already has status === 'active' (previously connected and not disconnected).
Common situations: Re-running a connect script after a successful earlier run; retry logic blindly retrying connect after a partial failure; redeployments that re-invoke connect on boot; environment reuse sharing the same storage database.
Related errors
- Slack refresh token is invalid. Get fresh tokens from https:
- Token rotation failed: ${data.error}
- Token rotation returned incomplete data
- App creation failed: ${errorDetails}
- App creation returned incomplete data
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/b69e486794d4f292.
Report an issue: GitHub.