Pumpkin-MC/Pumpkin · error · WritingError
IO error
Error message
IO error: {0} What it means
WritingError::IoError wraps a std::io::Error (via #[from]) that occurred while writing an encoded packet to the underlying transport, typically the TCP stream. The packet serialization itself succeeded; the syscall/socket layer failed. It is the lowest-level failure in the write path and often means the connection is already broken.
Solutions
- Match on the inner io::ErrorKind: BrokenPipe/ConnectionReset/ConnectionAborted mean the peer is gone — close the connection, don't retry.
- Filter dead connections out of broadcast lists before sending.
- For WouldBlock/TimedOut, consider retrying or tearing down per your tolerance.
- Enable TCP keepalive and handle disconnect events promptly so writes target live sockets.
Example fix
// before: treating all write failures alike
packet.write(stream).await?;
// after: distinguish peer disconnects
if let Err(WritingError::IoError(e)) = packet.write(stream).await {
if matches!(e.kind(), ErrorKind::BrokenPipe | ErrorKind::ConnectionReset) {
debug!("client gone: {e}");
return Ok(()); // drop connection
}
return Err(e.into());
} Defensive patterns
Strategy: try-catch
Validate before calling
// skip connections known to be closed before broadcasting let targets = clients.iter().filter(|c| c.is_open()).collect::<Vec<_>>();
Try / catch
if let Err(WritingError::IoError(e)) = packet.write(&mut stream).await {
match e.kind() {
ErrorKind::BrokenPipe | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted => {
debug!("peer disconnected: {e}");
disconnect(peer_id).await;
}
_ => return Err(e.into()),
}
} Prevention
- Remove closed connections from broadcast lists promptly via disconnect events.
- Never retry writes after BrokenPipe/ConnectionReset; the socket is unusable.
- Monitor network stack health if IoError rates spike (NIC issues, fd exhaustion).
When it happens
Trigger: Any packet write (write_all on the TCP stream) when the socket is closed or reset by the peer (BrokenPipe, ConnectionReset), the send buffer is full, or the OS reports a network error.
Common situations: Server broadcasting a packet to a client that just disconnected (connection reset by peer); writing to a closed keep-alive connection; network outage mid-game; socket buffer exhaustion under heavy load.
Related errors
- EOF, Tried to read but No bytes left to consume
- incomplete
- Unknown Status Code
- Failed to read HTTP response body
- Marketplace HTTP error
AI-assisted analysis of Pumpkin-MC/Pumpkin@8d4639e25a (2026-09-09).
Data as JSON: /api/errors/3eea590b081de540.
Report an issue: GitHub.
Appendix: source
Thrown at crates/pumpkin-protocol/src/ser/mod.rs:39
#[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}")]
IoError(#[from] std::io::Error),
#[error("Serde failure: {0}")]
Serde(String),
#[error("Packet is not supported in Minecraft version {0:?}")]
UnsupportedVersion(JavaMinecraftVersion),
#[error("Failed to serialize packet: {0}")]
Message(String),
}
impl serde::ser::Error for WritingError {
fn custom<T: std::fmt::Display>(msg: T) -> Self {
Self::Serde(msg.to_string())
}
}
struct NetworkReadDataSource<'a, R: NetworkReadExt + ?Sized>(&'a mut R);
impl<'a, R: NetworkReadExt + ?Sized> pumpkin_nbt::deserializer::NbtDataSource<'a>View on GitHub (pinned to 8d4639e25a)