stalwartlabs/stalwart · warning · io::Error(InvalidData)

Invalid line: {raw_response}

Error message

Invalid line: {raw_response}

What it means

Raised while parsing a Pyzor UDP response when the value of the 'code' field cannot be parsed as a u32. The parser in pyzor_send_message splits each line on ':' and wraps parse failures in an InvalidData io::Error that embeds the entire raw response. It indicates the peer did not answer with a well-formed Pyzor key:value reply.

Source

Thrown at crates/spam-filter/src/modules/pyzor.rs:108

    let socket = UdpSocket::bind("0.0.0.0:0").await?;
    tokio::time::timeout(timeout, socket.send_to(message.as_bytes(), addr)).await??;

    let mut buffer = vec![0u8; 1024];
    let (size, _) = tokio::time::timeout(timeout, socket.recv_from(&mut buffer)).await??;

    let raw_response = std::str::from_utf8(&buffer[..size])
        .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?;
    let mut response = PyzorResponse {
        code: u32::MAX,
        count: u64::MAX,
        wl_count: u64::MAX,
    };

    for line in raw_response.lines() {
        if let Some((k, v)) = line.split_once(':') {
            if k.eq_ignore_ascii_case("code") {
                response.code = v.trim().parse().map_err(|_| {
                    std::io::Error::new(
                        std::io::ErrorKind::InvalidData,
                        format!("Invalid line: {raw_response}"),
                    )
                })?;
            } else if k.eq_ignore_ascii_case("count") {
                response.count = v.trim().parse().map_err(|_| {
                    std::io::Error::new(
                        std::io::ErrorKind::InvalidData,
                        format!("Invalid line: {raw_response}"),
                    )
                })?;
            } else if k.eq_ignore_ascii_case("wl-count") {
                response.wl_count = v.trim().parse().map_err(|_| {
                    std::io::Error::new(
                        std::io::ErrorKind::InvalidData,
                        format!("Invalid line: {raw_response}"),
                    )
                })?;

View on GitHub (pinned to e962003857)

Solutions

  1. Verify the configured Pyzor server address (config.address) is a genuine, reachable Pyzor server
  2. Probe the server manually (pyzor check) and inspect the raw reply it returns
  3. Log the raw_response embedded in this error's message to see the malformed payload
  4. Check for proxy/captive-portal interference with UDP traffic
  5. Switch to a healthy public Pyzor mirror or upgrade the server
Defensive patterns

Strategy: validation

Validate before calling

// validate a pyzor reply before trusting it
fn is_valid_pyzor_response(raw: &str) -> bool {
    raw.lines().any(|l| {
        l.split_once(':')
            .map(|(k, v)| k.eq_ignore_ascii_case("code") && v.trim().parse::<u32>().is_ok())
            .unwrap_or(false)
    })
}

Try / catch

match pyzor_check(&config, &message).await {
    Ok(score) => score,
    Err(e) if e.is::<trc::SpamEvent>() && e.reason_is_parse_failure() => {
        tracing::warn!("pyzor reply unparseable; skipping pyzor score");
        Default::default()
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: pyzor_send_message receives a UDP datagram containing a 'code:' line whose value is not a valid u32 (empty value, non-numeric text, negative number, or an HTML/human-readable error message). Called via pyzor_check.

Common situations: config.address pointing at a wrong host/port where a different service answers; Pyzor server returning an error page instead of protocol fields; captive portal or proxy intercepting UDP traffic; corrupted or truncated datagrams.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of stalwartlabs/stalwart@e962003857 (2026-09-06). Data as JSON: /api/errors/6818fd65086496c1. Report an issue: GitHub.