{"record":{"id":"dd9debe7bd6c56fc","repo":"CherryHQ/cherry-studio","slug":"telegram-bot-token-is-required","errorCode":null,"errorMessage":"Telegram bot token is required","messagePattern":"Telegram bot token is required","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/channels/adapters/telegram/TelegramAdapter.ts","lineNumber":61,"sourceCode":"  // never hit the cap. Instead reset only after the bot has polled cleanly for this window —\n  // so transient failures spread over the adapter's lifetime don't monotonically exhaust it.\n  private readonly stabilityResetMs = 60_000\n\n  constructor(config: ChannelAdapterConfig<'telegram'>) {\n    super(config)\n    const { bot_token, allowed_chat_ids } = config.channelConfig\n    this.botToken = bot_token\n    this.allowedChatIds = allowed_chat_ids ?? []\n    this.notifyChatIds = [...this.allowedChatIds]\n  }\n\n  protected override async checkReady(): Promise<boolean> {\n    return !!this.botToken\n  }\n\n  protected override async performConnect(_signal: AbortSignal): Promise<void> {\n    if (!this.botToken) {\n      throw new Error('Telegram bot token is required')\n    }\n    this.shouldStop = false\n    this.reconnectAttempts = 0\n    await this.startBot()\n  }\n\n  private async startBot(): Promise<void> {\n    const bot = new Bot(this.botToken)\n    this.bot = bot\n\n    // Auth middleware — must be first\n    bot.use(async (ctx, next) => {\n      const chatId = ctx.chat?.id?.toString()\n      if (this.allowedChatIds.length > 0 && (!chatId || !this.allowedChatIds.includes(chatId))) {\n        this.log.debug('Dropping message from unauthorized chat', { chatId })\n        return\n      }\n      await next()","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/channels/adapters/telegram/TelegramAdapter.ts#L43-L79","documentation":"Thrown by TelegramAdapter.performConnect() when this.botToken is falsy at connect time. The token is read from config.channelConfig.bot_token in the constructor (TelegramAdapter.ts:50) and never mutated, so this fires only when the channel was configured with an empty/missing token. checkReady() at line 55 returns false for the same condition, so a well-behaved caller gating on readiness should never reach the throw — this is a defensive guard for a caller that skips readiness.","triggerScenarios":"performConnect() is invoked while bot_token in the channel config is empty string, null, or undefined. The checkReady() guard returns false on the same condition, so this throw fires only when the adapter is connected without a readiness check (e.g. a code path that calls connect() unconditionally, or a race where config was cleared between checkReady and performConnect).","commonSituations":"The user created a Telegram channel in agent settings but never pasted a bot token; the config store was reset/corrupted; a migration left bot_token empty; the UI allowed enabling the channel without validating the token field.","solutions":["Ensure the Telegram channel config has a non-empty bot_token before calling connect — the checkReady() guard exists for exactly this (TelegramAdapter.ts:55).","In the UI, mark the bot token field required and disable the connect action until filled.","Validate bot_token format at config-write time: Telegram tokens match /^\\d{9,10}:[A-Za-z0-9_-]{35}$.","If migrating, backfill bot_token from the legacy config store before enabling the channel."],"exampleFix":"// before — connect proceeds, then throws an opaque error\nawait adapter.connect(signal)\n\n// after — gate on readiness to avoid the throw entirely\nif (await adapter.checkReady()) {\n  await adapter.connect(signal)\n} else {\n  showConfigError('Telegram bot token is required')\n}","handlingStrategy":"validation","validationCode":"// Validate the Telegram bot token BEFORE constructing/connecting the adapter.\n// Telegram token format: <bot_id>:<auth_token>, e.g. 123456789:ABCdef...\nconst TELEGRAM_TOKEN_RE = /^\\d{8,10}:[A-Za-z0-9_-]{30,40}$/\n\nfunction isValidTelegramToken(v: unknown): v is string {\n  return typeof v === 'string' && TELEGRAM_TOKEN_RE.test(v)\n}\n\n// At channel-enable time:\nif (!isValidTelegramToken(config.channelConfig.bot_token)) {\n  return { ok: false, field: 'bot_token', reason: 'Telegram bot token is required (format: <id>:<secret>)' }\n}\nawait telegramAdapter.connect(signal)","typeGuard":"function hasTelegramBotToken(config: unknown): config is { bot_token: string } {\n  return (\n    typeof config === 'object' && config !== null &&\n    typeof (config as { bot_token?: unknown }).bot_token === 'string' &&\n    (config as { bot_token: string }).bot_token.length > 0\n  )\n}","tryCatchPattern":"// This throw is best prevented, not caught — it indicates a misconfigured channel.\n// If you must catch, treat it as a permanent config error (do not retry):\ntry {\n  await adapter.connect(signal)\n} catch (e) {\n  if (e instanceof Error && e.message === 'Telegram bot token is required') {\n    markChannelConfigInvalid(channelId, 'bot_token', 'Enter a Telegram bot token from @BotFather')\n    return // do not retry — config must change\n  }\n  throw e\n}","preventionTips":["Make the bot_token field required in the channel config UI and disable Connect until it is filled.","Validate the Telegram token regex at config-write time so malformed tokens never persist.","Use checkReady() (TelegramAdapter.ts:55) as the connect gate — it returns false for missing tokens.","Obtain the token from @BotFather on Telegram; never hand-edit it."],"tags":["telegram","config","validation","startup"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}