Tencent/WeKnora · error
decrypt file: %w
Error message
decrypt file: %w
What it means
DownloadFile fetched the AES-256-CBC encrypted media from the WeCom URL but decryptAESCBC failed while decrypting it with the message's aeskey. The underlying cause (bad base64, wrong key, corrupt data) is wrapped in this error. It indicates the media bytes cannot be recovered as-is.
Source
Thrown at internal/im/wecom/ws_adapter.go:120
if aesKeyB64 == "" {
// No encryption — return raw content (e.g. webhook mode uses media API)
return reader, fileName, nil
}
// Read all encrypted content
encryptedData, err := io.ReadAll(reader)
reader.Close()
if err != nil {
return nil, "", fmt.Errorf("read encrypted file: %w", err)
}
logger.Debugf(ctx, "[WeCom] Decrypting file: name=%s encrypted_size=%d aes_key_len=%d",
fileName, len(encryptedData), len(aesKeyB64))
// Decrypt
decrypted, err := decryptAESCBC(encryptedData, aesKeyB64)
if err != nil {
return nil, "", fmt.Errorf("decrypt file: %w", err)
}
logger.Debugf(ctx, "[WeCom] File decrypted: name=%s decrypted_size=%d", fileName, len(decrypted))
return io.NopCloser(bytes.NewReader(decrypted)), fileName, nil
}
// decryptAESCBC decrypts data encrypted with AES-256-CBC using PKCS#7 padding.
// The aesKeyB64 is the base64-encoded AES key provided per-message by WeCom.
// IV is the first 16 bytes of the decoded AES key.
func decryptAESCBC(ciphertext []byte, aesKeyB64 string) ([]byte, error) {
// WeCom's per-message aeskey is base64-encoded (43 chars → 32 bytes after decode)
aesKey, err := base64.StdEncoding.DecodeString(aesKeyB64 + "=")
if err != nil {
// Try without padding
aesKey, err = base64.RawStdEncoding.DecodeString(aesKeyB64)
if err != nil {
return nil, fmt.Errorf("base64 decode aes key: %w", err)View on GitHub (pinned to 988cbb0330)
Solutions
- Retry the download from the original URL to rule out truncation, then re-decrypt.
- Use the aeskey delivered in the SAME message as the FileKey — never pair a key with a different message's payload.
- Log len(encryptedData) and the base64 decode result; verify the key decodes to 16/24/32 bytes and padding is correct.
- Re-fetch the message if it came from a queue, since WeCom media URLs and keys are per-message and may expire.
Example fix
// before
decrypted, err := decryptAESCBC(encryptedData, staleKey)
// after
decrypted, err := decryptAESCBC(encryptedData, msg.AesKey) // key from same message
if err != nil {
// re-download once before failing
} Defensive patterns
Strategy: retry
Validate before calling
key, err := base64.StdEncoding.DecodeString(msg.AesKey)
if err != nil || len(key) < 16 {
return fmt.Errorf("invalid aes key for %s", msg.FileName)
} Type guard
func validAESKey(b64 string) bool {
k, err := base64.StdEncoding.DecodeString(b64)
return err == nil && (len(k) == 16 || len(k) == 24 || len(k) == 32)
} Try / catch
rc, name, err := adapter.DownloadFile(ctx, msg)
if err != nil && strings.HasPrefix(err.Error(), "decrypt file:") {
// re-download once, then surface decrypt failure with cause: err
rc, name, err = adapter.DownloadFile(ctx, msg)
} Prevention
- Always pair the aeskey with the FileKey from the same message.
- Re-download from the source URL before giving up — truncation is the top cause.
- Verify the download completed (size check) before decrypting.
- Keep WeCom media handling code in sync with current aibot encryption format.
When it happens
Trigger: decryptAESCBC returns an error inside DownloadFile — e.g. invalid base64 aes key, key/IV mismatch, ciphertext not a multiple of the block size, or bad PKCS#7 padding from truncated/corrupted downloads.
Common situations: Reusing a cached aeskey from a different message; partial network read producing truncated ciphertext; WeCom changing media encryption details; corrupted stored payload being re-processed.
Related errors
- aes key too short: %d bytes
- ciphertext too short
- ciphertext length is not a multiple of AES block size
- invalid padding
- plaintext too short
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/afe60b6927be6c06.
Report an issue: GitHub.