Pumpkin-MC/Pumpkin · warning · PacketError

Invalid packet string body

Error message

Invalid packet string body: {0}

What it means

RconError::InvalidBody is thrown while decoding a Source RCON packet: the bytes between the packet header and its null terminator are not valid UTF-8, so the string body (the payload or command text) cannot be converted into a Rust String. The RCON protocol frames each packet as length/id/type/body/NUL; this error means the body bytes failed str::from_utf8. It indicates malformed or hostile client input, since the vanilla RCON client always sends UTF-8.

Solutions

  1. Log the packet id/type and the failing bytes; fix or drop the offending client sending non-UTF-8 bodies.
  2. Ensure the sender encodes the body as UTF-8 before framing the packet.
  3. If robustness is desired, sanitize/reject non-UTF-8 bytes at the connection layer before calling the decoder.
  4. Verify packet length fields are parsed correctly so the body slice boundaries are right.

Example fix

// before: server accepts any byte stream and panics/errors deep in decode
let body = std::str::from_utf8(body_bytes)?;

// after: validate early and reject the client cleanly
if std::str::from_utf8(body_bytes).is_err() {
    connection.close("RCON body must be UTF-8");
    return;
}
Defensive patterns

Strategy: validation

Validate before calling

// Rust, before decoding: ensure the body slice is valid UTF-8
fn body_is_utf8(body: &[u8]) -> bool { std::str::from_utf8(body).is_ok() }
if !body_is_utf8(&body_bytes) {
    drop_connection("non-UTF-8 RCON body");
}

Type guard

fn as_str_body(b: &[u8]) -> Option<&str> { std::str::from_utf8(b).ok() }

Try / catch

match packet::decode(&buf) {
    Err(RconError::InvalidBody(e)) => debug!("dropping non-UTF-8 RCON packet: {e}"),
    Err(e) => warn!("rcon decode failed: {e}"),
    Ok(p) => handle(p),
}

Prevention

When it happens

Trigger: Deserializing an inbound serverbound RCON packet via Packet::decode/read when the body region contains bytes that are not valid UTF-8 (e.g. binary garbage, wrong-encoding multi-byte sequences, or a truncated/mis-framed packet that shifts byte boundaries so non-text bytes land in the body).

Common situations: A non-Minecraft tool or script speaking the RCON protocol with a wrong text encoding (e.g. sending Latin-1 or GBK bytes); a fuzzing or attack client sending arbitrary binary payloads; a length-field mismatch after packet corruption so non-body bytes are parsed as the body.

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 Pumpkin-MC/Pumpkin@8d4639e25a (2026-09-09). Data as JSON: /api/errors/75cf7a6c93add96f. Report an issue: GitHub.

Appendix: source

Thrown at crates/pumpkin-protocol/src/rcon.rs:58

        buf.put_i32_le(id);
        buf.put_i32_le(self as i32);
        let bytes = body.as_bytes();
        buf.put_slice(bytes);
        buf.put_u8(0);
        buf.put_u8(0);
        buf
    }
}

#[derive(Error, Debug)]
pub enum PacketError {
    #[error("Invalid length")]
    InvalidLength,
    #[error("Failed to send packet: {0}")]
    FailedSend(std::io::Error),
    #[error("Missing packet null terminator")]
    MissingNullTerminator,
    #[error("Invalid packet string body: {0}")]
    InvalidBody(std::str::Utf8Error),
    #[error("Unknown packet type: {0}")]
    UnknownPacketType(i32),
}

#[derive(Debug, PartialEq, Eq)]
/// Serverbound packet
pub struct Packet {
    id: i32,
    ptype: ServerboundPacket,
    body: Box<str>,
}

impl Packet {
    pub fn deserialize(incoming: &mut Vec<u8>) -> Result<Option<Self>, PacketError> {
        // We need at least 4 bytes to read the packet length header
        if incoming.len() < 4 {
            return Ok(None);

View on GitHub (pinned to 8d4639e25a)