chenhg5/cc-connect · error
wecom-ws: empty ciphertext
Error message
wecom-ws: empty ciphertext
What it means
wecomDecryptFile rejects an empty ciphertext slice before doing any crypto work. WeCom websocket media downloads are AES-256-CBC encrypted with IV = first 16 key bytes; decrypting zero bytes is meaningless, so the function fails fast rather than returning empty output.
Source
Thrown at platform/wecom/websocket_media.go:272
}
func isHexString(s string) bool {
for i := 0; i < len(s); i++ {
c := s[i]
switch {
case c >= '0' && c <= '9', c >= 'a' && c <= 'f', c >= 'A' && c <= 'F':
default:
return false
}
}
return true
}
// wecomDecryptFile decrypts payload from WeCom WS media URLs (AES-256-CBC, IV = first 16 key bytes).
// Same algorithm as @wecom/aibot-node-sdk decryptFile.
func wecomDecryptFile(ciphertext []byte, aesKeyB64 string) ([]byte, error) {
if len(ciphertext) == 0 {
return nil, fmt.Errorf("wecom-ws: empty ciphertext")
}
key, err := decodeWeComAESKey(aesKeyB64)
if err != nil {
return nil, err
}
key32 := key[:32]
iv := key32[:16]
block, err := aes.NewCipher(key32)
if err != nil {
return nil, err
}
if len(ciphertext)%aes.BlockSize != 0 {
return nil, fmt.Errorf("wecom-ws: ciphertext not multiple of block size")
}
plain := make([]byte, len(ciphertext))
cipher.NewCBCDecrypter(block, iv).CryptBlocks(plain, ciphertext)
return pkcs7UnpadWeCom(plain)View on GitHub (pinned to 4000b2338a)
Solutions
- Check len(raw) > 0 after the download and before decryption; log the HTTP status and headers.
- Re-request the media URL — WeCom media URLs are short-lived; fetch a fresh one via the API.
- If aesKey is set but server now returns plaintext, verify whether the URL is actually an encrypted WS media URL.
Example fix
// before
raw, _ := io.ReadAll(resp.Body)
return wecomDecryptFile(raw, aesKey)
// after
raw, _ := io.ReadAll(resp.Body)
if len(raw) == 0 {
return nil, fmt.Errorf("wecom media download returned empty body (status %s)", resp.Status)
}
return wecomDecryptFile(raw, aesKey) Defensive patterns
Strategy: try-catch
Validate before calling
if len(body) == 0 {
return fmt.Errorf("media download returned empty body")
} Try / catch
raw, err := downloadWeComWSMedia(url, key)
if err != nil {
if strings.Contains(err.Error(), "empty ciphertext") {
// fetch a fresh media URL and retry once
}
} Prevention
- Always check len(body) before decrypting.
- Log status + Content-Type on every media fetch.
- Treat empty 200s as expired-URL signals and re-request the media.
When it happens
Trigger: downloadWeComWSMedia passes the downloaded body to wecomDecryptFile when aesKey != "", but the HTTP response body was empty (0 bytes) — e.g. server returned 200 with no content, or a LimitReader consumed nothing.
Common situations: Expired or already-downloaded WeCom media URL returning an empty 200; a proxy stripping the body; fetch succeeded but the media was deleted server-side.
Related errors
- wecom-ws: invalid aeskey base64 length
- wecom-ws: decode aeskey: %w
- wecom-ws: aeskey decoded length %d, need >= 32
- wecom-ws: ciphertext not multiple of block size
- wecom-ws: empty padded data
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/f0e4eb5b86a069bb.
Report an issue: GitHub.