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

  1. Call await provider.disconnect(agentId) first, then reconnect.
  2. If the record is stale (app deleted in Slack), remove the installation record from storage (storage.deleteInstallation(id)) and reconnect.
  3. 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

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


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/b69e486794d4f292. Report an issue: GitHub.