sipeed/picoclaw · warning
media too large
Error message
media too large
What it means
Inbound media downloaded from the WeCom CDN exceeded wecomOutboundMediaMaxBytes = 20 MiB (media.go:28,301-302). The body is read through io.LimitReader(max+1); reading exactly max+1 bytes proves the object is over the cap and the download is aborted before decrypt/store. Note the per-type send limits are tighter (image/voice 2 MiB, video 10 MiB) but the inbound receive cap is a flat 20 MiB.
Source
Thrown at pkg/channels/wecom/media.go:302
req, err := http.NewRequestWithContext(ctx, http.MethodGet, resourceURL, nil)
if err != nil {
return "", fmt.Errorf("create request: %w", err)
}
resp, err := c.mediaClient.Do(req)
if err != nil {
return "", fmt.Errorf("download media: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("download media returned HTTP %d", resp.StatusCode)
}
data, err := io.ReadAll(io.LimitReader(resp.Body, wecomOutboundMediaMaxBytes+1))
if err != nil {
return "", fmt.Errorf("read media: %w", err)
}
if len(data) > wecomOutboundMediaMaxBytes {
return "", fmt.Errorf("media too large")
}
if aesKey != "" {
key, keyErr := decodeMediaAESKey(aesKey)
if keyErr != nil {
return "", keyErr
}
data, err = decryptAESCBC(key, data)
if err != nil {
return "", fmt.Errorf("decrypt media: %w", err)
}
}
filename, contentType := detectWeComMediaMetadata(
data,
msgID+fallbackExt,
resp.Header.Get("Content-Type"),
resourceURL,View on GitHub (pinned to 49183d7e8d)
Solutions
- Tell the sender the attachment exceeds 20 MiB and ask for a compressed version or a link
- Pre-check size before the full download: HEAD/Content-Length or an early-abort Range read, reject > 20 MiB without transferring
- If you self-host and accept the upstream implications, fork and raise wecomOutboundMediaMaxBytes - but WeCom's own upload path tops out at 100 chunks x 512 KiB (~50 MiB)
- Convert inbound oversized files into links at the source (upload to a file service, send the URL)
Example fix
// before: full download then size check
data, err := io.ReadAll(io.LimitReader(resp.Body, max+1))
if len(data) > max { return "", fmt.Errorf("media too large") }
// after: reject early from Content-Length when present
if n, err := strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64); err == nil && n > int64(max) {
return "", fmt.Errorf("media too large")
} Defensive patterns
Strategy: validation
Validate before calling
// before triggering an inbound fetch, probe the CDN object size
func sizeWithinLimit(ctx context.Context, rawurl string, max int64) bool {
req, err := http.NewRequestWithContext(ctx, http.MethodHead, rawurl, nil)
if err != nil {
return true // cannot pre-check; runtime check still applies
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return true
}
defer resp.Body.Close()
return resp.ContentLength < 0 || resp.ContentLength <= max
} Prevention
- pre-check Content-Length before downloading attachments
- educate senders: files > 20 MiB cannot transit this channel
- if raising the cap in a fork, keep it under the upload ceiling (100 x 512 KiB) or sends will fail later
When it happens
Trigger: A WeCom chat message carries a file or video whose CDN object is larger than 20 MiB (or whose encrypted ciphertext is still > 20 MiB before decryption); len(data) == 20 MiB+1 after the limited read.
Common situations: Users forwarding large videos, HD screen recordings, or log archives through WeCom; encrypted attachments whose ciphertext slightly exceeds the plaintext size; senders on unlimited plans unaware of the receiver cap.
Related errors
- unsupported wecom media type or size for %q
- failed to get WeCom QR code: response missing scode or auth_
- WeCom QR scan succeeded but bot credentials are missing
- invalid WeCom QR generate URL: %w
- invalid WeCom QR query URL: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/0de975c4d8997729.
Report an issue: GitHub.