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

  1. Check the file size against WeCom limits before sending: image/voice <= 2MB, video <= 10MB, any other file <= 20MB, minimum 5 bytes
  2. Re-encode or downscale the media (compress images, lower video bitrate, transcode audio) until it fits the per-kind cap
  3. Force part.Type to "file" for documents that do not need image/voice/video treatment, as long as they are <= 20MB
  4. Verify the resolved filename/content-type: give the attachment a proper extension (.jpg/.png/.mp3/.mp4 etc.) so content-type detection succeeds
  5. 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

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


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/b88c7d4a35d7d2c1. Report an issue: GitHub.