CherryHQ/cherry-studio · error · Error

Telegram bot token is required

Error message

Telegram bot token is required

What it means

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.

Source

Thrown at src/main/ai/channels/adapters/telegram/TelegramAdapter.ts:61

  // never hit the cap. Instead reset only after the bot has polled cleanly for this window —
  // so transient failures spread over the adapter's lifetime don't monotonically exhaust it.
  private readonly stabilityResetMs = 60_000

  constructor(config: ChannelAdapterConfig<'telegram'>) {
    super(config)
    const { bot_token, allowed_chat_ids } = config.channelConfig
    this.botToken = bot_token
    this.allowedChatIds = allowed_chat_ids ?? []
    this.notifyChatIds = [...this.allowedChatIds]
  }

  protected override async checkReady(): Promise<boolean> {
    return !!this.botToken
  }

  protected override async performConnect(_signal: AbortSignal): Promise<void> {
    if (!this.botToken) {
      throw new Error('Telegram bot token is required')
    }
    this.shouldStop = false
    this.reconnectAttempts = 0
    await this.startBot()
  }

  private async startBot(): Promise<void> {
    const bot = new Bot(this.botToken)
    this.bot = bot

    // Auth middleware — must be first
    bot.use(async (ctx, next) => {
      const chatId = ctx.chat?.id?.toString()
      if (this.allowedChatIds.length > 0 && (!chatId || !this.allowedChatIds.includes(chatId))) {
        this.log.debug('Dropping message from unauthorized chat', { chatId })
        return
      }
      await next()

View on GitHub (pinned to 726446b54c)

Solutions

  1. Ensure the Telegram channel config has a non-empty bot_token before calling connect — the checkReady() guard exists for exactly this (TelegramAdapter.ts:55).
  2. In the UI, mark the bot token field required and disable the connect action until filled.
  3. Validate bot_token format at config-write time: Telegram tokens match /^\d{9,10}:[A-Za-z0-9_-]{35}$.
  4. If migrating, backfill bot_token from the legacy config store before enabling the channel.

Example fix

// before — connect proceeds, then throws an opaque error
await adapter.connect(signal)

// after — gate on readiness to avoid the throw entirely
if (await adapter.checkReady()) {
  await adapter.connect(signal)
} else {
  showConfigError('Telegram bot token is required')
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate the Telegram bot token BEFORE constructing/connecting the adapter.
// Telegram token format: <bot_id>:<auth_token>, e.g. 123456789:ABCdef...
const TELEGRAM_TOKEN_RE = /^\d{8,10}:[A-Za-z0-9_-]{30,40}$/

function isValidTelegramToken(v: unknown): v is string {
  return typeof v === 'string' && TELEGRAM_TOKEN_RE.test(v)
}

// At channel-enable time:
if (!isValidTelegramToken(config.channelConfig.bot_token)) {
  return { ok: false, field: 'bot_token', reason: 'Telegram bot token is required (format: <id>:<secret>)' }
}
await telegramAdapter.connect(signal)

Type guard

function hasTelegramBotToken(config: unknown): config is { bot_token: string } {
  return (
    typeof config === 'object' && config !== null &&
    typeof (config as { bot_token?: unknown }).bot_token === 'string' &&
    (config as { bot_token: string }).bot_token.length > 0
  )
}

Try / catch

// This throw is best prevented, not caught — it indicates a misconfigured channel.
// If you must catch, treat it as a permanent config error (do not retry):
try {
  await adapter.connect(signal)
} catch (e) {
  if (e instanceof Error && e.message === 'Telegram bot token is required') {
    markChannelConfigInvalid(channelId, 'bot_token', 'Enter a Telegram bot token from @BotFather')
    return // do not retry — config must change
  }
  throw e
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/dd9debe7bd6c56fc. Report an issue: GitHub.