{"record":{"id":"d89c17fa41e66b7f","repo":"stalwartlabs/stalwart","slug":"invalid-response-raw-response","errorCode":null,"errorMessage":"Invalid response: {raw_response}","messagePattern":"Invalid response: (.+?)","errorType":"exception","errorClass":"io::Error(InvalidData)","httpStatus":null,"severity":"warning","filePath":"crates/spam-filter/src/modules/pyzor.rs","lineNumber":134,"sourceCode":"                        std::io::ErrorKind::InvalidData,\n                        format!(\"Invalid line: {raw_response}\"),\n                    )\n                })?;\n            } else if k.eq_ignore_ascii_case(\"wl-count\") {\n                response.wl_count = v.trim().parse().map_err(|_| {\n                    std::io::Error::new(\n                        std::io::ErrorKind::InvalidData,\n                        format!(\"Invalid line: {raw_response}\"),\n                    )\n                })?;\n            }\n        }\n    }\n\n    if response.code != u32::MAX && response.count != u64::MAX && response.wl_count != u64::MAX {\n        Ok(response)\n    } else {\n        Err(std::io::Error::new(\n            std::io::ErrorKind::InvalidData,\n            format!(\"Invalid response: {raw_response}\"),\n        ))\n    }\n}\n\ntrait PyzorWrite {\n    fn write_all(&mut self, data: &[u8]);\n}\n\nimpl PyzorWrite for Vec<u8> {\n    fn write_all(&mut self, data: &[u8]) {\n        self.extend_from_slice(data);\n    }\n}\n\nimpl PyzorWrite for Sha1 {\n    fn write_all(&mut self, data: &[u8]) {","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/stalwartlabs/stalwart/blob/e96200385781a6a9995a8b839ac27d6c75a983ee/crates/spam-filter/src/modules/pyzor.rs#L116-L152","documentation":"Raised when a Pyzor UDP response parsed successfully but one or more required fields (code, count, wl-count) are missing, leaving them at their sentinel values u32::MAX / u64::MAX. pyzor_send_message treats an incomplete result as invalid and includes the whole raw payload in the error message.","triggerScenarios":"pyzor_send_message receives a valid UTF-8 datagram that lacks code:, count:, or wl-count: lines — e.g. an empty datagram, a bare error string, or a partial protocol reply.","commonSituations":"Overloaded or degraded Pyzor server returning partial replies; wrong port hitting a service that responds but not in Pyzor protocol; datagram truncation losing trailing fields; server rejecting a digest without full counts.","solutions":["Verify the Pyzor server address/port configuration matches a genuine Pyzor server","Probe the server manually and inspect the complete raw response","Log raw_response from the error to determine which fields are missing","Switch to a healthy Pyzor mirror or upgrade the server","Handle the PyzorError event in pyzor_check so spam scoring degrades gracefully"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// check the reply contains all required fields before use\nfn is_complete_pyzor_response(raw: &str) -> bool {\n    let has = |key: &str| raw.lines().any(|l| {\n        l.split_once(':').map(|(k, _)| k.eq_ignore_ascii_case(key)).unwrap_or(false)\n    });\n    has(\"code\") && has(\"count\") && has(\"wl-count\")\n}","typeGuard":null,"tryCatchPattern":"match pyzor_check(&config, &message).await {\n    Ok(score) => score,\n    Err(e) if e.details().contains(\"Invalid response\") => {\n        tracing::warn!(\"incomplete pyzor reply; skipping signal\");\n        Default::default()\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Monitor the configured Pyzor server for degraded/partial replies and fail over to a mirror","Verify the endpoint port speaks Pyzor protocol, not another service","Log the raw response (included in the error) to see which fields go missing","Never block mail delivery on incomplete Pyzor data"],"tags":["parsing","udp","spam-filter","pyzor"],"backgroundTag":"unexpected-response-shape","analyzedSha":"e96200385781a6a9995a8b839ac27d6c75a983ee","analyzedAt":"2026-09-06T22:07:17.982Z","contentChangedAt":"2026-09-06T22:07:17.982Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}