CherryHQ/cherry-studio · error · Error

Message text cannot be empty.

Error message

Message text cannot be empty.

What it means

Thrown by WeixinBot.sendText() as a hard precondition guard before forwarding text to the WeChat send-message API. The WeChat protocol rejects empty messages server-side, so this check fails fast with a clear message rather than waiting for a round-trip API rejection. This is a private method invoked from reply() and send().

Source

Thrown at src/main/ai/channels/adapters/wechat/WeChatProtocol.ts:941

    logger.info('Long-poll loop stopped')
  }

  private async ensureCredentials(): Promise<Credentials> {
    if (this.credentials) return this.credentials

    const stored = await loadCredentials(this.tokenPath!)
    if (stored) {
      this.credentials = stored
      this.baseUrl = normalizeBaseUrl(stored.baseUrl)
      return stored
    }

    return this.login()
  }

  private async sendText(userId: string, text: string, contextToken: string): Promise<void> {
    if (text.length === 0) {
      throw new Error('Message text cannot be empty.')
    }

    const credentials = await this.ensureCredentials()
    await apiSendMessage(this.baseUrl, credentials.token, this.uin, buildTextMessage(userId, contextToken, text))
  }

  private async dispatchMessage(message: IncomingMessage): Promise<void> {
    if (this.handlers.length === 0) return

    const results = await Promise.allSettled(this.handlers.map(async (handler) => handler(message)))
    for (const result of results) {
      if (result.status === 'rejected') {
        this.reportError(result.reason)
      }
    }
  }

  private rememberContext(message: WeixinMessage): void {

View on GitHub (pinned to 726446b54c)

Solutions

  1. Check the text content before calling reply/send — if the LLM produced an empty response, skip the send or substitute a fallback message.
  2. Trace upstream: inspect sanitizeChannelOutput and any content-stripping transform to confirm it is not over-aggressively removing all content.
  3. If the empty string is legitimate (e.g., a 'typing' placeholder), reconsider the control flow — sendTyping/sendStopTyping exist for that purpose.

Example fix

// before
await this.bot.reply(message, text)

// after
if (text.trim().length === 0) {
  logger.warn('Skipping empty reply', { userId: message.userId })
  return
}
await this.bot.reply(message, text)
Defensive patterns

Strategy: validation

Validate before calling

// Validate text before sending
function isValidMessageText(text: string): boolean {
  return typeof text === 'string' && text.length > 0
}

if (!isValidMessageText(text)) {
  logger.warn('Skipping send — message text is empty', { userId })
  return
}

Type guard

function isNonEmptyString(value: unknown): value is string {
  return typeof value === 'string' && value.length > 0
}

Prevention

When it happens

Trigger: Called from WeixinBot.reply(message, text) (line 739) or WeixinBot.send(userId, text) (line 771) when the text argument is an empty string. The upstream caller is typically a channel adapter that renders an LLM response — if the model produced an empty completion or all content was stripped during sanitization, the empty string reaches sendText.

Common situations: An LLM completion returned only whitespace or content that was entirely filtered by sanitizeChannelOutput; a message-templating bug produced an empty string; a streaming reply flushed with an empty buffer after all chunks were consumed by an earlier transform.

Related errors


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