mastra-ai/mastra · error

Agent "${agentId}" is already connected to Telegram. Disconn

Error message

Agent "${agentId}" is already connected to Telegram. Disconnect first to reconnect.

What it means

connect() checks the store for an existing Telegram installation for the given agentId. If one exists with status 'active', connecting again would duplicate webhooks/polling loops, so the library refuses and tells you to disconnect first. This is an idempotency guard, not a transient failure.

Source

Thrown at channels/telegram/src/telegram-provider.ts:212

    this.#adapters.clear();
    this.#initPromise = null;
    if (wasInitialized) await this.initialize();
  }

  /**
   * Connect an agent to a Telegram bot.
   *
   * - With `options.botToken`: validate via `getMe`, mint a per-bot webhook
   *   secret, persist the installation, register the transport (webhook or
   *   polling), and return `{ type: 'immediate' }`.
   * - Without a token: persist a pending installation and return
   *   `{ type: 'deep_link' }` pointing at BotFather.
   */
  async connect(agentId: string, options: TelegramConnectOptions = {}): Promise<ChannelConnectResult> {
    const store = await this.#getStore();
    const existing = await store.getByAgent(agentId);
    if (existing?.status === 'active') {
      throw new Error(`Agent "${agentId}" is already connected to Telegram. Disconnect first to reconnect.`);
    }

    if (!options.botToken) {
      const installationId = existing?.id ?? randomUUID();
      await store.save({
        id: installationId,
        agentId,
        webhookId: existing?.webhookId ?? randomUUID(),
        status: 'pending',
        installedAt: existing?.installedAt ?? new Date(),
      });
      return { type: 'deep_link', url: BOTFATHER_DEEP_LINK, installationId };
    }

    const me = await getMe(options.botToken, this.#apiBaseUrl());
    const installationId = existing?.id ?? randomUUID();
    const webhookId = existing?.webhookId ?? randomUUID();
    const baseUrl = this.#getBaseUrl();

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Call await provider.disconnect(agentId) before connecting again
  2. Check the installation status first via the store/getByAgent and skip connect when status is 'active'
  3. If the active record is stale (agent deleted, bot removed), remove the installation from the store then reconnect
  4. Ensure application startup code is idempotent so connect runs only once per agent

Example fix

// before
await provider.connect('my-agent', { botToken })
// after
const existing = await provider.getInstallation?.('my-agent');
if (!existing || existing.status !== 'active') {
  await provider.connect('my-agent', { botToken });
}
Defensive patterns

Strategy: try-catch

Validate before calling

const existing = await store.getByAgent(agentId);
if (existing?.status === 'active') return; // already connected, skip
await provider.connect(agentId, { botToken });

Type guard

function isActiveInstallation(i: { status: string } | null | undefined): boolean {
  return i?.status === 'active';
}

Try / catch

try {
  await provider.connect(agentId, { botToken });
} catch (err) {
  if (err instanceof Error && err.message.includes('already connected')) {
    return; // treat as success — idempotent startup
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling provider.connect(agentId, ...) twice without an intervening disconnect(agentId), or connecting after a previous session left the installation active in the store.

Common situations: App restarts that re-run connect on boot while the installation persisted as active; hot-reload in dev re-invoking connect; retry logic that doesn't treat this as 'already done'; running two instances against the same store.

Related errors


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