Pumpkin-MC/Pumpkin · warning
Invalid UTF-8 sequence
Error message
Invalid UTF-8 sequence
What it means
Thrown by `Self::from_utf8` in the `PacketRead for String` impl when the bytes read for the string are not valid UTF-8. The protocol mandates UTF-8 strings, so undecodable bytes cause a hard error. This typically indicates data corruption or a stream desync rather than a normal runtime condition.
Solutions
- Check the stream for desync before this point (all prior fields parsed correctly?)
- Validate packet bytes with a UTF-8 validator or std::str::from_utf8 in a debug path to locate the offset
- Drop or reject the offending packet; resynchronize or reconnect
- Confirm the peer encodes strings as UTF-8 per the protocol version
Example fix
// before
let s: String = reader.read()?;
// after
if let Err(e) = String::read(&mut reader) {
log::warn!("non-UTF-8 string in packet: {e}");
return Err(PacketError::Malformed);
} Defensive patterns
Strategy: try-catch
Validate before calling
// validate UTF-8 in debug builds at decode boundaries debug_assert!(std::str::from_utf8(buf).is_ok(), "non-UTF-8 payload");
Type guard
fn is_utf8(b: &[u8]) -> bool { std::str::from_utf8(b).is_ok() } Try / catch
match String::read(&mut reader) {
Ok(s) => s,
Err(e) => { log::warn!("utf8 decode failed: {e}"); return Err(PacketError::Malformed); }
} Prevention
- Reject packets from peers with mismatched protocol versions
- Detect desync early: validate each field and abort on first error
- Never trust raw network bytes; treat decode failures as connection-fatal
When it happens
Trigger: read() reads `len` bytes after a valid VarUInt prefix, but those bytes fail String::from_utf8 — corrupted stream, wrong length prefix, or non-UTF-8 encoding from the peer.
Common situations: Desynced packet stream reading binary payload bytes as a string; malicious packets with arbitrary bytes; reading a legacy/compressed payload incorrectly.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
AI-assisted analysis of Pumpkin-MC/Pumpkin@8d4639e25a (2026-09-09).
Data as JSON: /api/errors/9171f341ed096e8e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/pumpkin-protocol/src/serial/deserializer.rs:169
impl PacketRead for String {
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
const MAX_STRING_LENGTH: usize = 32767;
let len = VarUInt::read(reader)?.0 as usize;
if len > MAX_STRING_LENGTH {
return Err(Error::new(
ErrorKind::InvalidData,
format!("String length {len} exceeds maximum of {MAX_STRING_LENGTH}"),
));
}
let mut buf = vec![0u8; len];
reader.read_exact(&mut buf)?;
Self::from_utf8(buf)
.map_err(|_| Error::new(ErrorKind::InvalidData, "Invalid UTF-8 sequence"))
}
}
impl<T: PacketRead> PacketRead for Vec<T> {
fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
let len = VarUInt::read(reader)?.0 as usize;
if len > 65536 {
return Err(Error::new(
ErrorKind::InvalidData,
format!("Vector length {len} exceeds limit of 65536"),
));
}
let mut buf = Self::with_capacity(len.min(1024));
for _ in 0..len {
buf.push(T::read(reader)?);
}
Ok(buf)
}View on GitHub (pinned to 8d4639e25a)