{"record":{"id":"9bed731eab0e8bbe","repo":"CherryHQ/cherry-studio","slug":"discord-bot-token-is-required","errorCode":null,"errorMessage":"Discord bot token is required","messagePattern":"Discord bot token is required","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/channels/adapters/discord/DiscordAdapter.ts","lineNumber":253,"sourceCode":"  private readonly reconnectDelays = [1000, 2000, 5000, 10000, 30000, 60000]\n  private readonly maxReconnectAttempts = 50\n  /** Per-chat streaming controller. One stream at a time per chat. */\n  private readonly streamingControllers = new Map<string, DiscordStreamingController>()\n\n  constructor(config: ChannelAdapterConfig<'discord'>) {\n    super(config)\n    const { bot_token, allowed_channel_ids } = config.channelConfig\n    this.botToken = bot_token\n    this.allowedChannelIds = allowed_channel_ids ?? []\n    this.notifyChatIds = [...this.allowedChannelIds]\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) throw new Error('Discord bot token is required')\n    this.shouldStop = false\n    await this.startGateway()\n    this.log.info('Discord bot started')\n  }\n\n  protected override async performDisconnect(): Promise<void> {\n    this.shouldStop = true\n    for (const controller of this.streamingControllers.values()) {\n      controller.dispose()\n    }\n    this.streamingControllers.clear()\n    this.cleanup()\n    this.log.info('Discord bot stopped')\n  }\n\n  // ─── Gateway Connection ───────────────────────────────────────\n\n  private async getGatewayUrl(): Promise<string> {","sourceCodeStart":235,"sourceCodeEnd":271,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/channels/adapters/discord/DiscordAdapter.ts#L235-L271","documentation":"Thrown by DiscordAdapter.performConnect during connection start when this.botToken is empty/falsy. checkReady already returns false without a token, so reaching performConnect with no token means the adapter was instructed to connect despite the gateway reporting not-ready — i.e. a connect was forced without configuring bot_token in channelConfig.","triggerScenarios":"DiscordAdapter.performConnect() runs (channel connect requested) while config.channelConfig.bot_token is empty or undefined.","commonSituations":"A Discord channel was created/saved without pasting a bot token; the token field was cleared; the channel config was imported/seeded without a token; the user clicked connect before completing setup.","solutions":["Open the Discord channel settings and paste a valid bot token into bot_token, then save.","Validate bot_token is non-empty before calling connect (and surface a setup prompt in the UI).","Keep checkReady gating connect so the adapter is not asked to connect in a not-ready state.","Regenerate the token in the Discord Developer Portal if it was revoked or never created."],"exampleFix":"// before: connect attempted with empty token\nawait discordAdapter.connect()\n\n// after: guard on readiness\nif (await discordAdapter.checkReady()) {\n  await discordAdapter.connect()\n} else {\n  throw new Error('Configure the Discord bot token before connecting')\n}","handlingStrategy":"validation","validationCode":"if (!adapter.botToken) {\n  // surface 'configure Discord bot token' in the UI; do not call connect()\n  throw new Error('Configure the Discord bot token before connecting')\n}\nif (await adapter.checkReady()) {\n  await adapter.connect()\n}","typeGuard":"function hasBotToken(config: { bot_token?: string }): boolean {\n  return typeof config.bot_token === 'string' && config.bot_token.trim().length > 0\n}","tryCatchPattern":"try {\n  await discordAdapter.connect()\n} catch (e) {\n  if (e instanceof Error && /Discord bot token is required/.test(e.message)) {\n    // mark the Discord channel as 'needs token' in the UI\n    logger.warn('Discord connect blocked: missing bot token')\n  } else throw e\n}","preventionTips":["Gate connect on checkReady so a tokenless adapter is never asked to connect.","Validate bot_token non-empty before saving the channel as ready.","Surface a setup prompt in the UI when the token is missing."],"tags":["channels","discord","config","auth","credentials"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}