zeroclaw-labs/zeroclaw · error
WeCom media aeskey too short: expected >= 32 bytes, got {}
Error message
WeCom media aeskey too short: expected >= 32 bytes, got {} What it means
WeCom media/message decryption needs an AES-256 key. The channel base64-decodes the configured aes_key (trying standard, standard-no-pad, and URL-safe alphabets) and requires at least 32 decoded bytes, using the first 32. Anything shorter is a fatal configuration error — a correct WeCom EncodingAESKey is 43 base64 characters decoding to exactly 32 bytes.
Source
Thrown at crates/zeroclaw-channels/src/wecom_ws.rs:264
}
}
// ── MediaDecryptor (per-attachment AES key) ──────────────────────────
struct MediaDecryptor;
impl MediaDecryptor {
/// Decrypt WeCom media attachment using per-message AES key.
/// AES-256-CBC, IV = first 16 bytes of key, WeCom-style PKCS padding.
fn decrypt(aeskey_b64: &str, encrypted: &[u8]) -> Result<Vec<u8>> {
let raw_key = base64::engine::general_purpose::STANDARD
.decode(aeskey_b64.trim())
.or_else(|_| base64::engine::general_purpose::STANDARD_NO_PAD.decode(aeskey_b64.trim()))
.or_else(|_| base64::engine::general_purpose::URL_SAFE.decode(aeskey_b64.trim()))
.context("failed to decode WeCom media aeskey")?;
if raw_key.len() < 32 {
anyhow::bail!(
"WeCom media aeskey too short: expected >= 32 bytes, got {}",
raw_key.len()
);
}
let key = &raw_key[..32];
let iv = &key[..16];
let mut buf = encrypted.to_vec();
let plaintext = cbc::Decryptor::<Aes256>::new(key.into(), iv.into())
.decrypt_padded_mut::<NoPadding>(&mut buf)
.map_err(|e| {
anyhow::Error::msg(format!("failed to decrypt WeCom media attachment: {e}"))
})?;
Ok(strip_wecom_padding(plaintext)?.to_vec())
}
}
View on GitHub (pinned to 88bb9c8533)
Solutions
- Re-copy the 43-character EncodingAESKey from the WeCom app admin page — it decodes to exactly 32 bytes
- Check the config/env value for truncation, whitespace, or shell-quoting damage
- Verify offline: decode the value with base64; the result must be at least 32 bytes
- Reload the channel after fixing the key
Example fix
# before aes_key = "EqQJ1F6cG0uXb2Zt" # truncated # after aes_key = "EqQJ1F6cG0uXb2Zt9wDhN3sRlOaYpKcVeTiBmQ4fXwg" # 43-char EncodingAESKey
Defensive patterns
Strategy: validation
Validate before calling
use base64::Engine;
fn valid_wecom_aeskey(key: &str) -> bool {
let k = key.trim();
[
&base64::engine::general_purpose::STANDARD,
&base64::engine::general_purpose::STANDARD_NO_PAD,
&base64::engine::general_purpose::URL_SAFE,
]
.iter()
.any(|e| e.decode(k).map(|raw| raw.len() >= 32).unwrap_or(false))
}
assert!(valid_wecom_aeskey(&cfg.aes_key), "aes_key must decode to >= 32 bytes"); Try / catch
Treat this as a fatal config error: catch it at channel startup, log 'aes_key invalid for app <id>', and keep the channel disabled rather than decrypting garbage.
Prevention
- Validate the aes_key at config load (43 chars / 32 decoded bytes), not on first media
- Store keys in a secret manager rather than inline files prone to truncation
- Remember the EncodingAESKey is app-specific: rotate it together with the app's token when credentials change
When it happens
Trigger: Creating/connecting the wecom_ws channel with an aes_key that decodes to fewer than 32 bytes: a truncated key, a value encoded with a wrong scheme, or a placeholder string.
Common situations: Copy-paste dropping trailing characters; the EncodingAESKey copied from a different WeCom app (keys are app-specific); the raw 32-byte key stored as-is instead of its 43-char base64 form; env-var interpolation producing an empty or shortened string.
Related errors
- WeCom WebSocket stream_mode={} is not supported; use partial
- WeCom max_file_size_bytes is zero
- invalid WeCom padding: empty payload
- invalid WeCom padding length
- unsupported room visibility '{other}': expected private or p
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/7ab38033d16c7e3f.
Report an issue: GitHub.