{"record":{"id":"eb4c354d9423b516","repo":"Pumpkin-MC/Pumpkin","slug":"unexpected-eof","errorCode":null,"errorMessage":"unexpected EOF","messagePattern":"unexpected EOF","errorType":"exception","errorClass":"Error::Incomplete","httpStatus":null,"severity":"error","filePath":"crates/pumpkin-nbt/src/deserializer.rs","lineNumber":89,"sourceCode":"        self.read_exact(&mut buf).map_err(Error::Incomplete)?;\n        Ok(buf[0])\n    }\n\n    fn read_bytes(&mut self, buf: &mut [u8]) -> Result<()> {\n        self.read_exact(buf).map_err(Error::Incomplete)\n    }\n\n    fn seek_relative(&mut self, offset: i64) -> Result<()> {\n        self.seek(SeekFrom::Current(offset))\n            .map_err(Error::Incomplete)?;\n        Ok(())\n    }\n\n    fn read_string(&mut self, len: usize) -> Result<Cow<'a, str>> {\n        let pos = self.position() as usize;\n        let data_len = self.get_ref().len();\n        if pos + len > data_len {\n            return Err(Error::Incomplete(std::io::Error::new(\n                std::io::ErrorKind::UnexpectedEof,\n                \"unexpected EOF\",\n            )));\n        }\n        self.set_position((pos + len) as u64);\n        let data = self.get_ref();\n        let slice = &data[pos..pos + len];\n        if let Ok(s) = std::str::from_utf8(slice) {\n            Ok(Cow::Borrowed(s))\n        } else {\n            let string = cesu8::from_java_cesu8(slice).map_err(|_| Error::Cesu8DecodingError)?;\n            Ok(string)\n        }\n    }\n\n    fn read_byte_array(&mut self, len: usize) -> Result<Cow<'a, [i8]>> {\n        let pos = self.position() as usize;\n        let data_len = self.get_ref().len();","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/Pumpkin-MC/Pumpkin/blob/8d4639e25a57c15e47448ec327c780d41bbf2356/crates/pumpkin-nbt/src/deserializer.rs#L71-L107","documentation":"Raised in read_string when the declared string length extends past the end of the underlying byte buffer. The library wraps an io::ErrorKind::UnexpectedEof in Error::Incomplete, meaning the NBT document was cut short.","triggerScenarios":"NBT::deserialize called on a buffer where a string's length prefix points beyond available bytes — typically a truncated payload or a wrong starting offset.","commonSituations":"Packets split across network reads and passed to the deserializer before fully buffered, truncated level.dat files, or decoding NBT from the wrong offset in a larger byte stream.","solutions":["Ensure the entire NBT payload is buffered before deserializing","Verify the read offset is at the true start of the NBT document","Check the transport for truncation (packet size limits, read loops)"],"exampleFix":"// before\nlet nbt = Nbt::deserialize(&packet_bytes[offset..]).unwrap();\n// after\nlet slice = &packet_bytes[offset..];\nif slice.len() < expected_min_len { return Err(TruncatedPacket); }\nlet nbt = Nbt::deserialize(slice)?;","handlingStrategy":"validation","validationCode":"fn slice_fits(buf: &[u8], pos: usize, len: usize) -> bool { pos.checked_add(len).map_or(false, |end| end <= buf.len()) }","typeGuard":null,"tryCatchPattern":"match result { Err(Error::Incomplete(e)) if e.kind() == UnexpectedEof => handle_truncated(), Ok(v) => use(v), Err(e) => propagate(e) }","preventionTips":["Accumulate the full packet before deserializing NBT","Verify offsets when NBT is embedded in larger streams","Persist/read files atomically to avoid truncation"],"tags":["nbt","eof","truncated-data","deserialization"],"backgroundTag":"unexpected-eof","analyzedSha":"8d4639e25a57c15e47448ec327c780d41bbf2356","analyzedAt":"2026-09-09T15:32:22.916Z","contentChangedAt":"2026-09-09T15:32:22.916Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}