zeroclaw-labs/zeroclaw · error
invalid WeCom padding: empty payload
Error message
invalid WeCom padding: empty payload
What it means
After AES decryption, WeCom payloads are unpadded by reading the last byte as the pad length (strip_wecom_padding, wecom_ws.rs:1864). An empty decrypted payload has no last byte and bails here. In practice the ciphertext decrypted to zero bytes: wrong aes_key or an empty/invalid frame from upstream.
Source
Thrown at crates/zeroclaw-channels/src/wecom_ws.rs:1866
async fn cancel_draft(&self, _recipient: &str, message_id: &str) -> Result<()> {
let req_id = self
.req_id_map
.lock()
.remove(message_id)
.unwrap_or_default();
if !req_id.is_empty() {
self.ws_send_respond_msg(&req_id, message_id, "", true)
.await?;
}
Ok(())
}
}
// ── Helper functions ─────────────────────────────────────────────────
fn strip_wecom_padding(input: &[u8]) -> Result<&[u8]> {
let Some(last) = input.last() else {
anyhow::bail!("invalid WeCom padding: empty payload");
};
let pad_len = *last as usize;
if pad_len == 0 || pad_len > 32 || pad_len > input.len() {
anyhow::bail!("invalid WeCom padding length");
}
Ok(&input[..input.len() - pad_len])
}
fn is_wecom_data_version_conflict_error(err: &anyhow::Error) -> bool {
let msg = err.to_string();
msg.contains("errcode=6000") || msg.contains("data version conflict")
}
fn parse_inbound_payload(payload: Value) -> Result<ParsedInbound> {
let msg_type = payload
.get("msgtype")
.and_then(Value::as_str)
.unwrap_or("")View on GitHub (pinned to 88bb9c8533)
Solutions
- Verify the aes_key matches the exact WeCom app (43-char EncodingAESKey decoding to 32 bytes)
- Log the ciphertext length before decrypt — zero-length input indicts the fetch/upstream, not the key
- Skip and dead-letter the message; empty payloads are deterministic and retries will not help
Defensive patterns
Strategy: try-catch
Validate before calling
// Guard the decrypt path: empty payloads are skipped before unpadding
if ciphertext.is_empty() {
return Ok(None); // nothing to decrypt
} Try / catch
Catch decrypt/padding errors per message, log msg_id and payload length, and skip that message; if every message fails, treat it as key drift — disable the channel and fix the aes_key rather than skipping forever.
Prevention
- Validate the aes_key at startup (decodes to >= 32 bytes)
- Alert when padding/decrypt failures exceed a threshold — it signals credential mismatch
- Never retry padding errors; they are deterministic
When it happens
Trigger: The decrypt path receives an empty input: a mismatched aes_key producing empty plaintext, an upstream empty media body, or a malformed frame that skipped the real ciphertext.
Common situations: aes_key from a different WeCom app or environment (test vs prod); a zero-byte attachment routed through the decrypt path; upstream sending an empty body on error.
Related errors
- invalid WeCom padding length
- WeCom media aeskey too short: expected >= 32 bytes, got {}
- WeCom channel requires the `channel-wecom` feature
- WeCom WebSocket channel requires the `channel-wecom-ws` feat
- wecom_ws channel is not connected
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/7b058c35234c00ec.
Report an issue: GitHub.