{"record":{"id":"afe60b6927be6c06","repo":"Tencent/WeKnora","slug":"decrypt-file-w","errorCode":null,"errorMessage":"decrypt file: %w","messagePattern":"decrypt file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/im/wecom/ws_adapter.go","lineNumber":120,"sourceCode":"\tif aesKeyB64 == \"\" {\n\t\t// No encryption — return raw content (e.g. webhook mode uses media API)\n\t\treturn reader, fileName, nil\n\t}\n\n\t// Read all encrypted content\n\tencryptedData, err := io.ReadAll(reader)\n\treader.Close()\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"read encrypted file: %w\", err)\n\t}\n\n\tlogger.Debugf(ctx, \"[WeCom] Decrypting file: name=%s encrypted_size=%d aes_key_len=%d\",\n\t\tfileName, len(encryptedData), len(aesKeyB64))\n\n\t// Decrypt\n\tdecrypted, err := decryptAESCBC(encryptedData, aesKeyB64)\n\tif err != nil {\n\t\treturn nil, \"\", fmt.Errorf(\"decrypt file: %w\", err)\n\t}\n\n\tlogger.Debugf(ctx, \"[WeCom] File decrypted: name=%s decrypted_size=%d\", fileName, len(decrypted))\n\n\treturn io.NopCloser(bytes.NewReader(decrypted)), fileName, nil\n}\n\n// decryptAESCBC decrypts data encrypted with AES-256-CBC using PKCS#7 padding.\n// The aesKeyB64 is the base64-encoded AES key provided per-message by WeCom.\n// IV is the first 16 bytes of the decoded AES key.\nfunc decryptAESCBC(ciphertext []byte, aesKeyB64 string) ([]byte, error) {\n\t// WeCom's per-message aeskey is base64-encoded (43 chars → 32 bytes after decode)\n\taesKey, err := base64.StdEncoding.DecodeString(aesKeyB64 + \"=\")\n\tif err != nil {\n\t\t// Try without padding\n\t\taesKey, err = base64.RawStdEncoding.DecodeString(aesKeyB64)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"base64 decode aes key: %w\", err)","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/im/wecom/ws_adapter.go#L102-L138","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ndecrypted, err := decryptAESCBC(encryptedData, staleKey)\n// after\ndecrypted, err := decryptAESCBC(encryptedData, msg.AesKey) // key from same message\nif err != nil {\n    // re-download once before failing\n}","handlingStrategy":"retry","validationCode":"key, err := base64.StdEncoding.DecodeString(msg.AesKey)\nif err != nil || len(key) < 16 {\n    return fmt.Errorf(\"invalid aes key for %s\", msg.FileName)\n}","typeGuard":"func validAESKey(b64 string) bool {\n    k, err := base64.StdEncoding.DecodeString(b64)\n    return err == nil && (len(k) == 16 || len(k) == 24 || len(k) == 32)\n}","tryCatchPattern":"rc, name, err := adapter.DownloadFile(ctx, msg)\nif err != nil && strings.HasPrefix(err.Error(), \"decrypt file:\") {\n    // re-download once, then surface decrypt failure with cause: err\n    rc, name, err = adapter.DownloadFile(ctx, msg)\n}","preventionTips":["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."],"tags":["wecom","encryption","aes","file-download"],"backgroundTag":"file-decrypt-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}