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
- 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.
- Filter outbound attachments by channel capability: route documents only to channels that support them, send a placeholder message to WeChat.
- 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.
- 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
- Maintain a per-channel capability matrix (WeChat: images only; Slack/Telegram: documents) and route attachments accordingly.
- Validate media_type at the dispatcher before selecting the adapter, not inside the adapter.
- For PDFs/code files, render to PNG before sending to WeChat, or send a download link as text.
- Do not retry on this error — it is deterministic for a given media_type.
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
- Invalid credentials format in ${tokenPath}
- Message text cannot be empty.
- Private key must be a non-empty string
- Invalid PEM format: missing BEGIN/END markers or key content
- Private key content is empty after cleaning
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/0e8aaf4ed993e19a.
Report an issue: GitHub.