{"record":{"id":"67f53fe0ee2edc70","repo":"mastra-ai/mastra","slug":"agent-agentid-is-already-connected-to-telegra","errorCode":null,"errorMessage":"Agent \"${agentId}\" is already connected to Telegram. Disconnect first to reconnect.","messagePattern":"Agent \"(.+?)\" is already connected to Telegram\\. Disconnect first to reconnect\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"channels/telegram/src/telegram-provider.ts","lineNumber":212,"sourceCode":"    this.#adapters.clear();\n    this.#initPromise = null;\n    if (wasInitialized) await this.initialize();\n  }\n\n  /**\n   * Connect an agent to a Telegram bot.\n   *\n   * - With `options.botToken`: validate via `getMe`, mint a per-bot webhook\n   *   secret, persist the installation, register the transport (webhook or\n   *   polling), and return `{ type: 'immediate' }`.\n   * - Without a token: persist a pending installation and return\n   *   `{ type: 'deep_link' }` pointing at BotFather.\n   */\n  async connect(agentId: string, options: TelegramConnectOptions = {}): Promise<ChannelConnectResult> {\n    const store = await this.#getStore();\n    const existing = await store.getByAgent(agentId);\n    if (existing?.status === 'active') {\n      throw new Error(`Agent \"${agentId}\" is already connected to Telegram. Disconnect first to reconnect.`);\n    }\n\n    if (!options.botToken) {\n      const installationId = existing?.id ?? randomUUID();\n      await store.save({\n        id: installationId,\n        agentId,\n        webhookId: existing?.webhookId ?? randomUUID(),\n        status: 'pending',\n        installedAt: existing?.installedAt ?? new Date(),\n      });\n      return { type: 'deep_link', url: BOTFATHER_DEEP_LINK, installationId };\n    }\n\n    const me = await getMe(options.botToken, this.#apiBaseUrl());\n    const installationId = existing?.id ?? randomUUID();\n    const webhookId = existing?.webhookId ?? randomUUID();\n    const baseUrl = this.#getBaseUrl();","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/channels/telegram/src/telegram-provider.ts#L194-L230","documentation":"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.","triggerScenarios":"Calling provider.connect(agentId, ...) twice without an intervening disconnect(agentId), or connecting after a previous session left the installation active in the store.","commonSituations":"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.","solutions":["Call await provider.disconnect(agentId) before connecting again","Check the installation status first via the store/getByAgent and skip connect when status is 'active'","If the active record is stale (agent deleted, bot removed), remove the installation from the store then reconnect","Ensure application startup code is idempotent so connect runs only once per agent"],"exampleFix":"// before\nawait provider.connect('my-agent', { botToken })\n// after\nconst existing = await provider.getInstallation?.('my-agent');\nif (!existing || existing.status !== 'active') {\n  await provider.connect('my-agent', { botToken });\n}","handlingStrategy":"try-catch","validationCode":"const existing = await store.getByAgent(agentId);\nif (existing?.status === 'active') return; // already connected, skip\nawait provider.connect(agentId, { botToken });","typeGuard":"function isActiveInstallation(i: { status: string } | null | undefined): boolean {\n  return i?.status === 'active';\n}","tryCatchPattern":"try {\n  await provider.connect(agentId, { botToken });\n} catch (err) {\n  if (err instanceof Error && err.message.includes('already connected')) {\n    return; // treat as success — idempotent startup\n  }\n  throw err;\n}","preventionTips":["Make connect-on-startup idempotent: check status before connecting","Call disconnect in shutdown/cleanup hooks so state stays consistent","Avoid running multiple app instances sharing one store without coordination","In dev, clear or key the store per process to avoid stale active records after hot reload"],"tags":["telegram","state-conflict","configuration"],"backgroundTag":"already-connected","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}