{"record":{"id":"98db1ca91bfdc1a9","repo":"zeroclaw-labs/zeroclaw","slug":"missing-msgtype","errorCode":null,"errorMessage":"missing msgtype","messagePattern":"missing msgtype","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/wecom_ws.rs","lineNumber":1887,"sourceCode":"    if pad_len == 0 || pad_len > 32 || pad_len > input.len() {\n        anyhow::bail!(\"invalid WeCom padding length\");\n    }\n    Ok(&input[..input.len() - pad_len])\n}\n\nfn is_wecom_data_version_conflict_error(err: &anyhow::Error) -> bool {\n    let msg = err.to_string();\n    msg.contains(\"errcode=6000\") || msg.contains(\"data version conflict\")\n}\n\nfn parse_inbound_payload(payload: Value) -> Result<ParsedInbound> {\n    let msg_type = payload\n        .get(\"msgtype\")\n        .and_then(Value::as_str)\n        .unwrap_or(\"\")\n        .to_string();\n    if msg_type.is_empty() {\n        anyhow::bail!(\"missing msgtype\");\n    }\n\n    let msg_id = payload\n        .get(\"msgid\")\n        .and_then(Value::as_str)\n        .unwrap_or(\"\")\n        .to_string();\n\n    let chat_type = payload\n        .get(\"chattype\")\n        .and_then(Value::as_str)\n        .unwrap_or(\"single\")\n        .to_string();\n\n    let chat_id = payload\n        .get(\"chatid\")\n        .and_then(Value::as_str)\n        .map(ToOwned::to_owned);","sourceCodeStart":1869,"sourceCodeEnd":1905,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/wecom_ws.rs#L1869-L1905","documentation":"The WeCom WS inbound parser requires a non-empty msgtype field on every payload it normalizes; a missing or empty string bails immediately, guarding the downstream dispatch that switches on message type.","triggerScenarios":"A WeCom callback/event payload without msgtype — control frames, status events, or new message types — or a schema change renaming the field; occasionally a mangled JSON body.","commonSituations":"WeCom introducing event payloads the parser does not model yet; version skew between the WeCom API and this channel; a proxy altering the JSON body.","solutions":["Log the raw payload (msg_id is parsed nearby) and identify which WeCom event lacks msgtype","Skip non-message events instead of failing the whole inbound batch","Report or patch the parser if the payload is a legitimate new event type","Check for JSON mangling middleware if msgtype should be present"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-check the raw payload before handing it to the normalizer\nlet has_msgtype = payload\n    .get(\"msgtype\")\n    .and_then(|v| v.as_str())\n    .is_some_and(|s| !s.is_empty());\nif !has_msgtype {\n    debug!(\"skipping non-message event: {payload}\");\n    return Ok(None);\n}","typeGuard":null,"tryCatchPattern":"Parse each inbound payload independently; on 'missing msgtype' log the raw JSON and continue with the next message instead of propagating out of the receive loop.","preventionTips":["Pin the channel version against a tested WeCom API behavior","Monitor skipped-message counters to detect schema drift early","Fuzz-test the parser with event payloads that omit message-only fields"],"tags":["wecom","message-parsing","schema","validation","inbound"],"backgroundTag":"missing-required-field","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}