sipeed/picoclaw · error
unsupported wecom media type or size for %q
Error message
unsupported wecom media type or size for %q
What it means
Thrown by the WeCom channel's chunked media uploader when outboundWeComMediaKind() cannot classify the file into one of the supported upload kinds (image, voice, video, file). Classification fails when the file is smaller than 5 bytes, larger than the per-kind cap (image 2MB, voice 2MB, video 10MB, file 20MB), or the part type/content-type/extension matches none of the allowed media types. The error is raised before any network call, right after os.ReadFile of the resolved local file.
Source
Thrown at pkg/channels/wecom/media.go:687
func (c *WeComChannel) uploadOutboundMedia(
ctx context.Context,
localPath, filename, contentType string,
part bus.MediaPart,
) (*wecomOutboundMedia, error) {
_ = ctx
contentType = detectLocalWeComContentType(localPath, contentType)
filename = ensureWeComOutboundFilename(filename, localPath, contentType)
data, err := os.ReadFile(localPath)
if err != nil {
return nil, fmt.Errorf("read media file: %w", err)
}
size := int64(len(data))
kind := outboundWeComMediaKind(part.Type, filename, contentType, size)
if kind == "" {
return nil, fmt.Errorf("unsupported wecom media type or size for %q", filename)
}
totalChunks := (len(data) + wecomUploadChunkMaxBytes - 1) / wecomUploadChunkMaxBytes
if totalChunks <= 0 || totalChunks > wecomUploadMaxChunks {
return nil, fmt.Errorf("wecom upload requires 1-%d chunks, got %d", wecomUploadMaxChunks, totalChunks)
}
sum := md5.Sum(data)
initEnv, err := c.sendCommandAck(wecomCommand{
Cmd: wecomCmdUploadMediaInit,
Headers: wecomHeaders{ReqID: randomID(10)},
Body: wecomUploadMediaInitBody{
Type: kind,
Filename: filename,
TotalSize: size,
TotalChunks: totalChunks,
MD5: hex.EncodeToString(sum[:]),
},View on GitHub (pinned to 49183d7e8d)
Solutions
- Check the file size against WeCom limits before sending: image/voice <= 2MB, video <= 10MB, any other file <= 20MB, minimum 5 bytes
- Re-encode or downscale the media (compress images, lower video bitrate, transcode audio) until it fits the per-kind cap
- Force part.Type to "file" for documents that do not need image/voice/video treatment, as long as they are <= 20MB
- Verify the resolved filename/content-type: give the attachment a proper extension (.jpg/.png/.mp3/.mp4 etc.) so content-type detection succeeds
- If the media cannot be shrunk, send a text/link reference instead of the binary
Example fix
// before
part := channels.MediaPart{Type: "image", Ref: imgRef} // img is 3.5MB -> error 660
// after
part := channels.MediaPart{Type: "image", Ref: shrunkImgRef} // re-encoded to <=2MB
// or downgrade to a plain file part if it is within 20MB
part := channels.MediaPart{Type: "file", Ref: imgRef} Defensive patterns
Strategy: validation
Validate before calling
// before send: classify locally with the same limits the uploader enforces
func wecomMediaKindOK(partType, filename, contentType string, size int64) error {
if size < 5 {
return fmt.Errorf("file too small: %d bytes (min 5)", size)
}
switch strings.ToLower(partType) {
case "image", "audio", "voice":
if size > 2<<20 {
return fmt.Errorf("%s exceeds 2MB", partType)
}
case "video":
if size > 10<<20 {
return fmt.Errorf("video exceeds 10MB")
}
default:
if size > 20<<20 {
return fmt.Errorf("file exceeds 20MB")
}
}
return nil
}
info, _ := os.Stat(localPath)
if err := wecomMediaKindOK(part.Type, filename, contentType, info.Size()); err != nil {
// re-encode, downscale, or drop the part instead of sending
} Type guard
func isWecomUnsupportedMedia(err error) bool {
return err != nil && strings.Contains(err.Error(), "unsupported wecom media type or size")
} Try / catch
if uploaded, err := ch.Send(msg); err != nil {
if isWecomUnsupportedMedia(err) {
// do not retry: size/type is deterministic. Downgrade to text or re-encode.
}
} Prevention
- Validate media size against 2MB image/voice, 10MB video, 20MB file caps before queueing
- Normalize filenames to carry a proper extension so content-type detection works
- Compress generated images/videos before attaching them to replies
- Reject or skip sub-5-byte placeholder files at generation time
When it happens
Trigger: Sending an outbound message with a media part where: part.Type is not file/image/audio/voice/video and no fallback applies; the content-type or file extension is not in the WeCom allowlist; the file exceeds wecomOutboundMediaMaxBytes (20MB) as a generic file or the per-kind limit; or the file is under 5 bytes (wecomUploadMinBytes). Concretely, any Send with parts whose resolved local file fails the size/size-kind matrix in outboundWeComMediaKind (pkg/channels/wecom/media.go:575).
Common situations: Bot replies with a generated image larger than 2MB; a TTS voice clip exceeds 2MB; a video attachment exceeds 10MB; an arbitrary document exceeds 20MB; an empty or 2-byte placeholder file is referenced; the attachment has an unusual extension with a generic application/octet-stream content type so no kind matches.
Related errors
- media too large
- wecom upload requires 1-%d chunks, got %d
- wecom upload init returned empty upload_id
- wecom upload finish returned empty media_id
- failed to get WeCom QR code: response missing scode or auth_
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/b88c7d4a35d7d2c1.
Report an issue: GitHub.