Pumpkin-MC/Pumpkin · info · ReadingError
EOF, Tried to read but No bytes left to consume
Error message
EOF, Tried to read {0} but No bytes left to consume What it means
ReadingError::CleanEOF is produced by the protocol's async reader when it attempts to read a field of the given size but the stream has ended with no bytes left to consume. It is the serde-style deserializer's way of reporting a clean end-of-stream: the peer closed the connection mid-decode expectation, with zero bytes available for the requested read.
Solutions
- Treat CleanEOF at packet boundaries as a normal disconnect: log at debug level and close the connection gracefully.
- Check for stream closure (read returning Ok(0)) before attempting to decode the next packet.
- Do not retry reads on a closed stream; re-establish the connection if communication must continue.
- If it happens mid-packet rather than at a boundary, check whether the sender truncates packets (see Incomplete).
Example fix
// before: logging every EOF as an error and spamming logs
error!("read failed: {e}");
// after: distinguish clean disconnects
match e {
ReadingError::CleanEOF(_) => debug!("client disconnected"),
other => error!("read failed: {other}"),
} Defensive patterns
Strategy: try-catch
Validate before calling
// check stream liveness before a read loop iteration
if stream.peek(&mut [0u8; 1]).await?.is_empty() { break; } // peer closed Try / catch
match reader.next_packet().await {
Err(ReadingError::CleanEOF(_)) => {
debug!("peer closed connection cleanly");
break; // normal disconnect path
}
Err(e) => warn!("read error: {e}"),
Ok(p) => handle(p),
} Prevention
- Treat end-of-stream as an expected event in connection loops, not an error.
- Enable TCP keepalive to detect dead peers faster.
- Never reuse a reader after a CleanEOF; recreate the connection if needed.
When it happens
Trigger: Calling any read/deserialize step (e.g. reading a VarInt, length prefix, or fixed-size buffer) on a TcpStream whose peer has closed; the reader tries to fill the requested byte count from an exhausted reader.
Common situations: A Minecraft client disconnecting right as the server awaits the next packet; a keep-alive/poll loop reading a closed socket; reading from a half-closed connection after a client crash or timeout on the peer side.
Related errors
AI-assisted analysis of Pumpkin-MC/Pumpkin@8d4639e25a (2026-09-09).
Data as JSON: /api/errors/77cd668c58b76c4e.
Report an issue: GitHub.
Appendix: source
Thrown at crates/pumpkin-protocol/src/ser/mod.rs:21
use std::io::{Read, Write};
use crate::{
FixedBitSet,
codec::{
bit_set::BitSet, var_int::VarInt, var_long::VarLong, var_uint::VarUInt, var_ulong::VarULong,
},
};
use pumpkin_nbt::{
compound::NbtCompound, deserializer::NbtReadHelper, serializer::NbtWriteHelperJava, tag::NbtTag,
};
use pumpkin_util::math::position::BlockPos;
use pumpkin_util::{text::TextComponent, version::JavaMinecraftVersion};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ReadingError {
#[error("EOF, Tried to read {0} but No bytes left to consume")]
CleanEOF(String),
#[error("incomplete: {0}")]
Incomplete(String),
#[error("too large: {0}")]
TooLarge(String),
#[error("{0}")]
Message(String),
}
impl serde::de::Error for ReadingError {
fn custom<T: std::fmt::Display>(msg: T) -> Self {
Self::Message(msg.to_string())
}
}
#[derive(Debug, Error)]
pub enum WritingError {
#[error("IO error: {0}")]View on GitHub (pinned to 8d4639e25a)