CherryHQ/cherry-studio · warning · Error

WeChat can only forward image files, not "${file.media_type}

Error message

WeChat can only forward image files, not "${file.media_type}" (${file.filename})

What it means

Thrown by WeChatAdapter.sendFile() when file.media_type does not start with 'image/'. This is a hard capability limit: the reverse-engineered WeChat iLink protocol (WeChatProtocol.ts) only implements outbound image upload via cdnUploadImage → WeixinBot.sendImage. There is no document/file upload path — the comment at WeChatAdapter.ts:109 notes 'Document upload would need protocol-level CDN work.' This is an intentional guard, not a transient failure.

Source

Thrown at src/main/ai/channels/adapters/wechat/WeChatAdapter.ts:112

    const chunks = splitMessage(text, WECHAT_MAX_LENGTH)

    for (let i = 0; i < chunks.length; i++) {
      await this.bot.send(chatId, chunks[i])

      if (i < chunks.length - 1) {
        await new Promise((resolve) => setTimeout(resolve, 100))
      }
    }
  }

  override async sendFile(chatId: string, file: FileAttachment): Promise<void> {
    if (!this.bot) {
      throw new Error('Bot is not connected')
    }
    // The reverse-engineered WeChat protocol only supports outbound images today
    // (WeixinBot.sendImage). Document upload would need protocol-level CDN work.
    if (!file.media_type.startsWith('image/')) {
      throw new Error(`WeChat can only forward image files, not "${file.media_type}" (${file.filename})`)
    }
    await this.bot.sendImage(chatId, Buffer.from(file.data, 'base64'))
    this.log.info('Sent file', { chatId, filename: file.filename, size: file.size })
  }

  async sendTypingIndicator(chatId: string): Promise<void> {
    if (!this.bot) {
      throw new Error('Bot is not connected')
    }

    try {
      await this.bot.sendTyping(chatId)
    } catch {
      // sendTyping requires a cached context_token from a prior message;
      // silently ignore if not yet available
    }
  }

View on GitHub (pinned to 726446b54c)

Solutions

  1. Convert non-image files to images before sending to WeChat (e.g. render a PDF page to PNG), or send a text description/link instead.
  2. Filter outbound attachments by channel capability: route documents only to channels that support them, send a placeholder message to WeChat.
  3. If document upload becomes required, implement the protocol-level CDN file upload in WeChatProtocol.ts (new media_type constant + cdnUploadFile) — the comment marks this as known unfinished work.
  4. Do not retry — this throw is deterministic for a given media_type; fix the input or skip the send.

Example fix

// before — non-image attachment throws and breaks the tool
await wechatAdapter.sendFile(chatId, { filename: 'report.pdf', media_type: 'application/pdf', data, size })

// after — branch on channel capability before sending
const supportsDocs = (type: string) => !type.startsWith('wechat')
if (file.media_type.startsWith('image/')) {
  await adapter.sendFile(chatId, file)
} else {
  await adapter.sendMessage(chatId, `[Unsupported on WeChat: ${file.filename}]`)
}
Defensive patterns

Strategy: validation

Validate before calling

// Filter by media type BEFORE calling sendFile. WeChat only accepts image/*.
const WECHAT_ALLOWED_MEDIA_PREFIX = 'image/'

function canWeChatSend(file: FileAttachment): boolean {
  return file.media_type.startsWith(WECHAT_ALLOWED_MEDIA_PREFIX)
}

// Usage in a multi-channel dispatcher:
if (file.media_type.startsWith('image/')) {
  await wechatAdapter.sendFile(chatId, file)
} else {
  await wechatAdapter.sendMessage(chatId, `[Non-image file not supported on WeChat: ${file.filename}]`)
}

Type guard

function isImageAttachment(file: FileAttachment): boolean {
  return typeof file.media_type === 'string' && file.media_type.startsWith('image/')
}

Try / catch

// This is a capability limit, not a transient error — do not retry.
try {
  await wechatAdapter.sendFile(chatId, file)
} catch (e) {
  if (e instanceof Error && /WeChat can only forward image files/.test(e.message)) {
    // Degrade to a text notice or convert the file to an image
    await wechatAdapter.sendMessage(chatId, `[${file.filename} not supported on WeChat]`)
    return
  }
  throw e
}

Prevention

When it happens

Trigger: sendFile() called with a FileAttachment whose media_type is application/pdf, text/plain, application/zip, video/*, audio/*, etc. The adapter posts images via bot.sendImage (WeChatAdapter.ts:114) which uploads to WeChat CDN with media_type:1 (image) — there is no code path for other types. The check is a startsWith('image/') prefix match, so 'image/png', 'image/jpeg', 'image/gif', 'image/webp' pass.

Common situations: The agent forwards a PDF, a code file, or a voice clip to WeChat; a tool output includes a non-image attachment; the agent is configured on multiple channels and a file type that Slack/Telegram accept (documents) is routed to WeChat uniformly.

Related errors


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