{"record":{"id":"9001ad5ad88139a8","repo":"CherryHQ/cherry-studio","slug":"message-text-cannot-be-empty","errorCode":null,"errorMessage":"Message text cannot be empty.","messagePattern":"Message text cannot be empty\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/channels/adapters/wechat/WeChatProtocol.ts","lineNumber":941,"sourceCode":"    logger.info('Long-poll loop stopped')\n  }\n\n  private async ensureCredentials(): Promise<Credentials> {\n    if (this.credentials) return this.credentials\n\n    const stored = await loadCredentials(this.tokenPath!)\n    if (stored) {\n      this.credentials = stored\n      this.baseUrl = normalizeBaseUrl(stored.baseUrl)\n      return stored\n    }\n\n    return this.login()\n  }\n\n  private async sendText(userId: string, text: string, contextToken: string): Promise<void> {\n    if (text.length === 0) {\n      throw new Error('Message text cannot be empty.')\n    }\n\n    const credentials = await this.ensureCredentials()\n    await apiSendMessage(this.baseUrl, credentials.token, this.uin, buildTextMessage(userId, contextToken, text))\n  }\n\n  private async dispatchMessage(message: IncomingMessage): Promise<void> {\n    if (this.handlers.length === 0) return\n\n    const results = await Promise.allSettled(this.handlers.map(async (handler) => handler(message)))\n    for (const result of results) {\n      if (result.status === 'rejected') {\n        this.reportError(result.reason)\n      }\n    }\n  }\n\n  private rememberContext(message: WeixinMessage): void {","sourceCodeStart":923,"sourceCodeEnd":959,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/channels/adapters/wechat/WeChatProtocol.ts#L923-L959","documentation":"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().","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the text content before calling reply/send — if the LLM produced an empty response, skip the send or substitute a fallback message.","Trace upstream: inspect sanitizeChannelOutput and any content-stripping transform to confirm it is not over-aggressively removing all content.","If the empty string is legitimate (e.g., a 'typing' placeholder), reconsider the control flow — sendTyping/sendStopTyping exist for that purpose."],"exampleFix":"// before\nawait this.bot.reply(message, text)\n\n// after\nif (text.trim().length === 0) {\n  logger.warn('Skipping empty reply', { userId: message.userId })\n  return\n}\nawait this.bot.reply(message, text)","handlingStrategy":"validation","validationCode":"// Validate text before sending\nfunction isValidMessageText(text: string): boolean {\n  return typeof text === 'string' && text.length > 0\n}\n\nif (!isValidMessageText(text)) {\n  logger.warn('Skipping send — message text is empty', { userId })\n  return\n}","typeGuard":"function isNonEmptyString(value: unknown): value is string {\n  return typeof value === 'string' && value.length > 0\n}","tryCatchPattern":null,"preventionTips":["Check text.trim().length > 0 before calling reply() or send().","Inspect sanitizeChannelOutput to ensure it does not strip all content from legitimate responses.","Handle empty LLM completions upstream — substitute a fallback message or retry the generation."],"tags":["wechat","validation","message","channels","precondition"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}