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

  1. Re-copy the 43-character EncodingAESKey from the WeCom app admin page — it decodes to exactly 32 bytes
  2. Check the config/env value for truncation, whitespace, or shell-quoting damage
  3. Verify offline: decode the value with base64; the result must be at least 32 bytes
  4. 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

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


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/7ab38033d16c7e3f. Report an issue: GitHub.